Merge from rustc

This commit is contained in:
Ralf Jung 2025-04-10 14:24:19 +02:00
commit f69ea4d82f
872 changed files with 7959 additions and 5020 deletions

View file

@ -2,7 +2,7 @@
name: Library Tracking Issue
about: A tracking issue for an unstable library feature.
title: Tracking Issue for XXX
labels: C-tracking-issue, T-libs-api
labels: C-tracking-issue, T-libs-api, S-tracking-unimplemented
---
<!--
Thank you for creating a tracking issue!
@ -49,6 +49,8 @@ For larger features, more steps might be involved.
If the feature is changed later, please add those PRs here as well.
-->
(Remember to update the `S-tracking-*` label when checking boxes.)
- [ ] Implementation: #...
- [ ] Final comment period (FCP)[^1]
- [ ] Stabilization PR

View file

@ -186,6 +186,48 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "askama"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a4e46abb203e00ef226442d452769233142bbfdd79c3941e84c8e61c4112543"
dependencies = [
"askama_derive",
"itoa",
"percent-encoding",
"serde",
"serde_json",
]
[[package]]
name = "askama_derive"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54398906821fd32c728135f7b351f0c7494ab95ae421d41b6f5a020e158f28a6"
dependencies = [
"askama_parser",
"basic-toml",
"memchr",
"proc-macro2",
"quote",
"rustc-hash 2.1.1",
"serde",
"serde_derive",
"syn 2.0.100",
]
[[package]]
name = "askama_parser"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
dependencies = [
"memchr",
"serde",
"serde_derive",
"winnow 0.7.4",
]
[[package]]
name = "autocfg"
version = "1.4.0"
@ -676,7 +718,6 @@ name = "compiletest"
version = "0.0.0"
dependencies = [
"anstyle-svg",
"anyhow",
"build_helper",
"colored",
"diff",
@ -1357,8 +1398,8 @@ name = "generate-copyright"
version = "0.1.0"
dependencies = [
"anyhow",
"askama",
"cargo_metadata 0.18.1",
"rinja",
"serde",
"serde_json",
"thiserror 1.0.69",
@ -3078,9 +3119,7 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5"
dependencies = [
"humansize",
"itoa",
"percent-encoding",
"rinja_derive",
]
@ -4598,6 +4637,7 @@ version = "0.0.0"
dependencies = [
"bitflags",
"derive-where",
"ena",
"indexmap",
"rustc-hash 1.1.0",
"rustc_ast_ir",
@ -4636,6 +4676,7 @@ name = "rustdoc"
version = "0.0.0"
dependencies = [
"arrayvec",
"askama",
"base64",
"expect-test",
"indexmap",
@ -4644,7 +4685,6 @@ dependencies = [
"pulldown-cmark 0.9.6",
"pulldown-cmark-escape",
"regex",
"rinja",
"rustdoc-json-types",
"serde",
"serde_json",
@ -5434,7 +5474,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
"winnow 0.5.40",
]
[[package]]
@ -6445,6 +6485,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
dependencies = [
"memchr",
]
[[package]]
name = "winsplit"
version = "0.1.0"

View file

@ -28,7 +28,7 @@
# - A new option
# - A change in the default values
#
# If the change-id does not match the version currently in use, x.py will
# If the change-id does not match the version currently in use, x.py will
# display the changes made to the bootstrap.
# To suppress these warnings, you can set change-id = "ignore".
#change-id = <latest change id in src/bootstrap/src/utils/change_tracker.rs>
@ -442,6 +442,9 @@
# What custom diff tool to use for displaying compiletest tests.
#compiletest-diff-tool = <none>
# Whether to use the precompiled stage0 libtest with compiletest.
#compiletest-use-stage0-libtest = true
# Indicates whether ccache is used when building certain artifacts (e.g. LLVM).
# Set to `true` to use the first `ccache` in PATH, or set an absolute path to use
# a specific version.

View file

@ -60,7 +60,6 @@ pub enum ExternAbi {
System {
unwind: bool,
},
RustIntrinsic,
RustCall,
/// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even
/// normally ABI-compatible Rust types can become ABI-incompatible with this ABI!
@ -128,7 +127,6 @@ abi_impls! {
RiscvInterruptS =><= "riscv-interrupt-s",
RustCall =><= "rust-call",
RustCold =><= "rust-cold",
RustIntrinsic =><= "rust-intrinsic",
Stdcall { unwind: false } =><= "stdcall",
Stdcall { unwind: true } =><= "stdcall-unwind",
System { unwind: false } =><= "system",
@ -199,7 +197,7 @@ impl ExternAbi {
/// - are subject to change between compiler versions
pub fn is_rustic_abi(self) -> bool {
use ExternAbi::*;
matches!(self, Rust | RustCall | RustIntrinsic | RustCold)
matches!(self, Rust | RustCall | RustCold)
}
pub fn supports_varargs(self) -> bool {

View file

@ -1829,7 +1829,7 @@ pub struct PointeeInfo {
pub safe: Option<PointerKind>,
/// If `safe` is `Some`, then the pointer is either null or dereferenceable for this many bytes.
/// On a function argument, "dereferenceable" here means "dereferenceable for the entire duration
/// of this function call", i.e. it is UB for the memory that this pointer points to to be freed
/// of this function call", i.e. it is UB for the memory that this pointer points to be freed
/// while this function is still running.
/// The size can be zero if the pointer is not dereferenceable.
pub size: Size,

View file

@ -563,6 +563,7 @@ impl Pat {
/// This is intended for use by diagnostics.
pub fn to_ty(&self) -> Option<P<Ty>> {
let kind = match &self.kind {
PatKind::Missing => unreachable!(),
// In a type expression `_` is an inference variable.
PatKind::Wild => TyKind::Infer,
// An IDENT pattern with no binding mode would be valid as path to a type. E.g. `u32`.
@ -625,7 +626,8 @@ impl Pat {
| PatKind::Guard(s, _) => s.walk(it),
// These patterns do not contain subpatterns, skip.
PatKind::Wild
PatKind::Missing
| PatKind::Wild
| PatKind::Rest
| PatKind::Never
| PatKind::Expr(_)
@ -676,6 +678,7 @@ impl Pat {
/// Return a name suitable for diagnostics.
pub fn descr(&self) -> Option<String> {
match &self.kind {
PatKind::Missing => unreachable!(),
PatKind::Wild => Some("_".to_string()),
PatKind::Ident(BindingMode::NONE, ident, None) => Some(format!("{ident}")),
PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())),
@ -769,6 +772,9 @@ pub enum RangeSyntax {
// Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum PatKind {
/// A missing pattern, e.g. for an anonymous param in a bare fn like `fn f(u32)`.
Missing,
/// Represents a wildcard pattern (`_`).
Wild,
@ -1169,6 +1175,7 @@ pub enum MacStmtStyle {
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Local {
pub id: NodeId,
pub super_: Option<Span>,
pub pat: P<Pat>,
pub ty: Option<P<Ty>>,
pub kind: LocalKind,
@ -3926,7 +3933,7 @@ mod size_asserts {
static_assert_size!(Item, 144);
static_assert_size!(ItemKind, 80);
static_assert_size!(LitKind, 24);
static_assert_size!(Local, 80);
static_assert_size!(Local, 96);
static_assert_size!(MetaItemLit, 40);
static_assert_size!(Param, 40);
static_assert_size!(Pat, 72);

View file

@ -92,6 +92,12 @@ pub struct AutoDiffAttrs {
pub input_activity: Vec<DiffActivity>,
}
impl AutoDiffAttrs {
pub fn has_primal_ret(&self) -> bool {
matches!(self.ret_activity, DiffActivity::Active | DiffActivity::Dual)
}
}
impl DiffMode {
pub fn is_rev(&self) -> bool {
matches!(self, DiffMode::Reverse)

View file

@ -704,7 +704,8 @@ fn walk_parenthesized_parameter_data<T: MutVisitor>(vis: &mut T, args: &mut Pare
}
fn walk_local<T: MutVisitor>(vis: &mut T, local: &mut P<Local>) {
let Local { id, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
visit_opt(super_, |sp| vis.visit_span(sp));
vis.visit_id(id);
visit_attrs(vis, attrs);
vis.visit_pat(pat);
@ -1587,7 +1588,7 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
vis.visit_id(id);
match kind {
PatKind::Err(_guar) => {}
PatKind::Wild | PatKind::Rest | PatKind::Never => {}
PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
PatKind::Ident(_binding_mode, ident, sub) => {
vis.visit_ident(ident);
visit_opt(sub, |sub| vis.visit_pat(sub));

View file

@ -323,7 +323,7 @@ pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::R
}
pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::Result {
let Local { id: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
let Local { id: _, super_: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
walk_list!(visitor, visit_attribute, attrs);
try_visit!(visitor.visit_pat(pat));
visit_opt!(visitor, visit_ty, ty);
@ -750,7 +750,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
try_visit!(visitor.visit_pat(subpattern));
try_visit!(visitor.visit_expr(guard_condition));
}
PatKind::Wild | PatKind::Rest | PatKind::Never => {}
PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
PatKind::Err(_guar) => {}
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
walk_list!(visitor, visit_pat, elems);

View file

@ -95,6 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> {
// Let statements are allowed to have impl trait in bindings.
let super_ = l.super_;
let ty = l.ty.as_ref().map(|t| {
self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
});
@ -109,7 +110,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let span = self.lower_span(l.span);
let source = hir::LocalSource::Normal;
self.lower_attrs(hir_id, &l.attrs, l.span);
self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source })
self.arena.alloc(hir::LetStmt { hir_id, super_, ty, pat, init, els, span, source })
}
fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {

View file

@ -1496,18 +1496,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Option<Ident>] {
self.arena.alloc_from_iter(decl.inputs.iter().map(|param| match param.pat.kind {
PatKind::Ident(_, ident, _) => {
if ident.name != kw::Empty {
Some(self.lower_ident(ident))
} else {
None
}
}
PatKind::Missing => None,
PatKind::Ident(_, ident, _) => Some(self.lower_ident(ident)),
PatKind::Wild => Some(Ident::new(kw::Underscore, self.lower_span(param.pat.span))),
_ => {
self.dcx().span_delayed_bug(
param.pat.span,
"non-ident/wild param pat must trigger an error",
"non-missing/ident/wild param pat must trigger an error",
);
None
}
@ -2223,6 +2218,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.attrs.insert(hir_id.local_id, a);
}
let local = hir::LetStmt {
super_: None,
hir_id,
init,
pat,

View file

@ -26,6 +26,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let pat_hir_id = self.lower_node_id(pattern.id);
let node = loop {
match &pattern.kind {
PatKind::Missing => break hir::PatKind::Missing,
PatKind::Wild => break hir::PatKind::Wild,
PatKind::Never => break hir::PatKind::Never,
PatKind::Ident(binding_mode, ident, sub) => {

View file

@ -79,10 +79,6 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
| ExternAbi::SysV64 { .. }
| ExternAbi::System { .. }
| ExternAbi::EfiApi => Ok(()),
// implementation details
ExternAbi::RustIntrinsic => {
Err(UnstableAbi { abi, feature: sym::intrinsics, explain: GateReason::ImplDetail })
}
ExternAbi::Unadjusted => {
Err(UnstableAbi { abi, feature: sym::abi_unadjusted, explain: GateReason::ImplDetail })
}

View file

@ -244,7 +244,7 @@ impl<'a> AstValidator<'a> {
fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option<Ident>, bool)) {
for Param { pat, .. } in &decl.inputs {
match pat.kind {
PatKind::Ident(BindingMode::NONE, _, None) | PatKind::Wild => {}
PatKind::Missing | PatKind::Ident(BindingMode::NONE, _, None) | PatKind::Wild => {}
PatKind::Ident(BindingMode::MUT, ident, None) => {
report_err(pat.span, Some(ident), true)
}

View file

@ -1336,6 +1336,9 @@ impl<'a> State<'a> {
self.print_outer_attributes(&loc.attrs);
self.space_if_not_bol();
self.ibox(INDENT_UNIT);
if loc.super_.is_some() {
self.word_nbsp("super");
}
self.word_nbsp("let");
self.ibox(INDENT_UNIT);
@ -1622,9 +1625,9 @@ impl<'a> State<'a> {
fn print_pat(&mut self, pat: &ast::Pat) {
self.maybe_print_comment(pat.span.lo());
self.ann.pre(self, AnnNode::Pat(pat));
/* Pat isn't normalized, but the beauty of it
is that it doesn't matter */
/* Pat isn't normalized, but the beauty of it is that it doesn't matter */
match &pat.kind {
PatKind::Missing => unreachable!(),
PatKind::Wild => self.word("_"),
PatKind::Never => self.word("!"),
PatKind::Ident(BindingMode(by_ref, mutbl), ident, sub) => {
@ -1946,12 +1949,7 @@ impl<'a> State<'a> {
if let Some(eself) = input.to_self() {
self.print_explicit_self(&eself);
} else {
let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
ident.name == kw::Empty
} else {
false
};
if !invalid {
if !matches!(input.pat.kind, PatKind::Missing) {
self.print_pat(&input.pat);
self.word(":");
self.space();

View file

@ -15,6 +15,7 @@ pub use super::polonius::legacy::{
RichLocation, RustcFacts,
};
pub use super::region_infer::RegionInferenceContext;
use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
/// Options determining the output behavior of [`get_body_with_borrowck_facts`].
///
@ -97,8 +98,9 @@ pub struct BodyWithBorrowckFacts<'tcx> {
/// * Polonius is highly unstable, so expect regular changes in its signature or other details.
pub fn get_body_with_borrowck_facts(
tcx: TyCtxt<'_>,
def: LocalDefId,
def_id: LocalDefId,
options: ConsumerOptions,
) -> BodyWithBorrowckFacts<'_> {
*super::do_mir_borrowck(tcx, def, Some(options)).1.unwrap()
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def_id);
*do_mir_borrowck(&mut root_cx, def_id, Some(options)).1.unwrap()
}

View file

@ -406,8 +406,8 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
// started MIR borrowchecking with, so the region
// constraints have already been taken. Use the data from
// our `mbcx` instead.
|vid| mbcx.regioncx.var_infos[vid].origin,
|vid| mbcx.regioncx.var_infos[vid].universe,
|vid| RegionVariableOrigin::Nll(mbcx.regioncx.definitions[vid].origin),
|vid| mbcx.regioncx.definitions[vid].universe,
)
}
}
@ -487,7 +487,7 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>(
let (sub_region, cause) = info?;
debug!(?sub_region, "cause = {:#?}", cause);
let error = match (error_region, *sub_region) {
let error = match (error_region, sub_region.kind()) {
(Some(error_region), ty::ReVar(vid)) => RegionResolutionError::SubSupConflict(
vid,
region_var_origin(vid),

View file

@ -8,9 +8,7 @@ use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::{self as hir, CoroutineKind, LangItem};
use rustc_index::IndexSlice;
use rustc_infer::infer::{
BoundRegionConversionTime, NllRegionVariableOrigin, RegionVariableOrigin,
};
use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
use rustc_infer::traits::SelectionError;
use rustc_middle::bug;
use rustc_middle::mir::{
@ -587,7 +585,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
// this by hooking into the pretty printer and telling it to label the
// lifetimes without names with the value `'0`.
if let ty::Ref(region, ..) = ty.kind() {
match **region {
match region.kind() {
ty::ReBound(_, ty::BoundRegion { kind: br, .. })
| ty::RePlaceholder(ty::PlaceholderRegion {
bound: ty::BoundRegion { kind: br, .. },
@ -607,7 +605,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
let region = if let ty::Ref(region, ..) = ty.kind() {
match **region {
match region.kind() {
ty::ReBound(_, ty::BoundRegion { kind: br, .. })
| ty::RePlaceholder(ty::PlaceholderRegion {
bound: ty::BoundRegion { kind: br, .. },
@ -633,9 +631,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
) {
let predicate_span = path.iter().find_map(|constraint| {
let outlived = constraint.sub;
if let Some(origin) = self.regioncx.var_infos.get(outlived)
&& let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_)) =
origin.origin
if let Some(origin) = self.regioncx.definitions.get(outlived)
&& let NllRegionVariableOrigin::Placeholder(_) = origin.origin
&& let ConstraintCategory::Predicate(span) = constraint.category
{
Some(span)

View file

@ -190,7 +190,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
fold_regions(tcx, ty, |region, _| match *region {
fold_regions(tcx, ty, |region, _| match region.kind() {
ty::ReVar(vid) => self.to_error_region(vid).unwrap_or(region),
_ => region,
})
@ -198,7 +198,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// Returns `true` if a closure is inferred to be an `FnMut` closure.
fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
if let Some(ty::ReLateParam(late_param)) = self.to_error_region(fr).as_deref()
if let Some(r) = self.to_error_region(fr)
&& let ty::ReLateParam(late_param) = r.kind()
&& let ty::LateParamRegionKind::ClosureEnv = late_param.kind
&& let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty
{
@ -832,7 +833,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if let (Some(f), Some(outlived_f)) =
(self.to_error_region(fr), self.to_error_region(outlived_fr))
{
if *outlived_f != ty::ReStatic {
if outlived_f.kind() != ty::ReStatic {
return;
}
let suitable_region = self.infcx.tcx.is_suitable_region(self.mir_def_id(), f);

View file

@ -288,7 +288,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
let tcx = self.infcx.tcx;
debug!("give_region_a_name: error_region = {:?}", error_region);
match *error_region {
match error_region.kind() {
ty::ReEarlyParam(ebr) => ebr.has_name().then(|| {
let def_id = tcx.generics_of(self.mir_def_id()).region_param(ebr, tcx).def_id;
let span = tcx.hir_span_if_local(def_id).unwrap_or(DUMMY_SP);
@ -896,7 +896,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
&self,
fr: RegionVid,
) -> Option<RegionName> {
let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else {
let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else {
return None;
};
if region.has_name() {
@ -912,7 +912,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
let found = tcx
.any_free_region_meets(&tcx.type_of(region_parent).instantiate_identity(), |r| {
*r == ty::ReEarlyParam(region)
r.kind() == ty::ReEarlyParam(region)
});
Some(RegionName {
@ -931,7 +931,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
&self,
fr: RegionVid,
) -> Option<RegionName> {
let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else {
let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else {
return None;
};
if region.has_name() {
@ -1007,7 +1007,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
if data.projection_term.self_ty() == ty => {}
_ => return false,
}
tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyParam(region))
tcx.any_free_region_meets(pred, |r| r.kind() == ty::ReEarlyParam(region))
})
} else {
false

View file

@ -9,6 +9,7 @@
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
@ -21,6 +22,7 @@ use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::{ControlFlow, Deref};
use root_cx::BorrowCheckRootCtxt;
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::dominators::Dominators;
@ -35,7 +37,9 @@ use rustc_infer::infer::{
};
use rustc_middle::mir::*;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode};
use rustc_middle::ty::{
self, ParamEnv, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypingMode, fold_regions,
};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::impls::{
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
@ -45,7 +49,7 @@ use rustc_mir_dataflow::move_paths::{
};
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
use rustc_span::{Span, Symbol};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use smallvec::SmallVec;
use tracing::{debug, instrument};
@ -73,7 +77,6 @@ mod def_use;
mod diagnostics;
mod member_constraints;
mod nll;
mod opaque_types;
mod path_utils;
mod place_ext;
mod places_conflict;
@ -81,6 +84,7 @@ mod polonius;
mod prefixes;
mod region_infer;
mod renumber;
mod root_cx;
mod session_diagnostics;
mod type_check;
mod universal_regions;
@ -102,44 +106,202 @@ pub fn provide(providers: &mut Providers) {
*providers = Providers { mir_borrowck, ..*providers };
}
fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
/// Provider for `query mir_borrowck`. Similar to `typeck`, this must
/// only be called for typeck roots which will then borrowck all
/// nested bodies as well.
fn mir_borrowck(
tcx: TyCtxt<'_>,
def: LocalDefId,
) -> Result<&ConcreteOpaqueTypes<'_>, ErrorGuaranteed> {
assert!(!tcx.is_typeck_child(def.to_def_id()));
let (input_body, _) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
let input_body: &Body<'_> = &input_body.borrow();
if input_body.should_skip() || input_body.tainted_by_errors.is_some() {
debug!("Skipping borrowck because of injected body or tainted body");
// Let's make up a borrowck result! Fun times!
let result = BorrowCheckResult {
concrete_opaque_types: FxIndexMap::default(),
closure_requirements: None,
used_mut_upvars: SmallVec::new(),
tainted_by_errors: input_body.tainted_by_errors,
};
return tcx.arena.alloc(result);
if let Some(guar) = input_body.tainted_by_errors {
debug!("Skipping borrowck because of tainted body");
Err(guar)
} else if input_body.should_skip() {
debug!("Skipping borrowck because of injected body");
let opaque_types = ConcreteOpaqueTypes(Default::default());
Ok(tcx.arena.alloc(opaque_types))
} else {
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def);
let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
do_mir_borrowck(&mut root_cx, def, None).0;
debug_assert!(closure_requirements.is_none());
debug_assert!(used_mut_upvars.is_empty());
root_cx.finalize()
}
}
/// Data propagated to the typeck parent by nested items.
/// This should always be empty for the typeck root.
#[derive(Debug)]
struct PropagatedBorrowCheckResults<'tcx> {
closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
used_mut_upvars: SmallVec<[FieldIdx; 8]>,
}
/// After we borrow check a closure, we are left with various
/// requirements that we have inferred between the free regions that
/// appear in the closure's signature or on its field types. These
/// requirements are then verified and proved by the closure's
/// creating function. This struct encodes those requirements.
///
/// The requirements are listed as being between various `RegionVid`. The 0th
/// region refers to `'static`; subsequent region vids refer to the free
/// regions that appear in the closure (or coroutine's) type, in order of
/// appearance. (This numbering is actually defined by the `UniversalRegions`
/// struct in the NLL region checker. See for example
/// `UniversalRegions::closure_mapping`.) Note the free regions in the
/// closure's signature and captures are erased.
///
/// Example: If type check produces a closure with the closure args:
///
/// ```text
/// ClosureArgs = [
/// 'a, // From the parent.
/// 'b,
/// i8, // the "closure kind"
/// for<'x> fn(&'<erased> &'x u32) -> &'x u32, // the "closure signature"
/// &'<erased> String, // some upvar
/// ]
/// ```
///
/// We would "renumber" each free region to a unique vid, as follows:
///
/// ```text
/// ClosureArgs = [
/// '1, // From the parent.
/// '2,
/// i8, // the "closure kind"
/// for<'x> fn(&'3 &'x u32) -> &'x u32, // the "closure signature"
/// &'4 String, // some upvar
/// ]
/// ```
///
/// Now the code might impose a requirement like `'1: '2`. When an
/// instance of the closure is created, the corresponding free regions
/// can be extracted from its type and constrained to have the given
/// outlives relationship.
#[derive(Clone, Debug)]
pub struct ClosureRegionRequirements<'tcx> {
/// The number of external regions defined on the closure. In our
/// example above, it would be 3 -- one for `'static`, then `'1`
/// and `'2`. This is just used for a sanity check later on, to
/// make sure that the number of regions we see at the callsite
/// matches.
pub num_external_vids: usize,
/// Requirements between the various free regions defined in
/// indices.
pub outlives_requirements: Vec<ClosureOutlivesRequirement<'tcx>>,
}
/// Indicates an outlives-constraint between a type or between two
/// free regions declared on the closure.
#[derive(Copy, Clone, Debug)]
pub struct ClosureOutlivesRequirement<'tcx> {
// This region or type ...
pub subject: ClosureOutlivesSubject<'tcx>,
// ... must outlive this one.
pub outlived_free_region: ty::RegionVid,
// If not, report an error here ...
pub blame_span: Span,
// ... due to this reason.
pub category: ConstraintCategory<'tcx>,
}
// Make sure this enum doesn't unintentionally grow
#[cfg(target_pointer_width = "64")]
rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16);
/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing
/// that must outlive some region.
#[derive(Copy, Clone, Debug)]
pub enum ClosureOutlivesSubject<'tcx> {
/// Subject is a type, typically a type parameter, but could also
/// be a projection. Indicates a requirement like `T: 'a` being
/// passed to the caller, where the type here is `T`.
Ty(ClosureOutlivesSubjectTy<'tcx>),
/// Subject is a free region from the closure. Indicates a requirement
/// like `'a: 'b` being passed to the caller; the region here is `'a`.
Region(ty::RegionVid),
}
/// Represents a `ty::Ty` for use in [`ClosureOutlivesSubject`].
///
/// This abstraction is necessary because the type may include `ReVar` regions,
/// which is what we use internally within NLL code, and they can't be used in
/// a query response.
#[derive(Copy, Clone, Debug)]
pub struct ClosureOutlivesSubjectTy<'tcx> {
inner: Ty<'tcx>,
}
// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this
// type is not recognized as a binder for late-bound region.
impl<'tcx, I> !TypeVisitable<I> for ClosureOutlivesSubjectTy<'tcx> {}
impl<'tcx, I> !TypeFoldable<I> for ClosureOutlivesSubjectTy<'tcx> {}
impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
/// All regions of `ty` must be of kind `ReVar` and must represent
/// universal regions *external* to the closure.
pub fn bind(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self {
let inner = fold_regions(tcx, ty, |r, depth| match r.kind() {
ty::ReVar(vid) => {
let br = ty::BoundRegion {
var: ty::BoundVar::from_usize(vid.index()),
kind: ty::BoundRegionKind::Anon,
};
ty::Region::new_bound(tcx, depth, br)
}
_ => bug!("unexpected region in ClosureOutlivesSubjectTy: {r:?}"),
});
Self { inner }
}
let borrowck_result = do_mir_borrowck(tcx, def, None).0;
debug!("mir_borrowck done");
tcx.arena.alloc(borrowck_result)
pub fn instantiate(
self,
tcx: TyCtxt<'tcx>,
mut map: impl FnMut(ty::RegionVid) -> ty::Region<'tcx>,
) -> Ty<'tcx> {
fold_regions(tcx, self.inner, |r, depth| match r.kind() {
ty::ReBound(debruijn, br) => {
debug_assert_eq!(debruijn, depth);
map(ty::RegionVid::from_usize(br.var.index()))
}
_ => bug!("unexpected region {r:?}"),
})
}
}
/// Perform the actual borrow checking.
///
/// Use `consumer_options: None` for the default behavior of returning
/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
/// to the given [`ConsumerOptions`].
#[instrument(skip(tcx), level = "debug")]
/// [`PropagatedBorrowCheckResults`] only. Otherwise, return [`BodyWithBorrowckFacts`]
/// according to the given [`ConsumerOptions`].
///
/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`.
#[instrument(skip(root_cx), level = "debug")]
fn do_mir_borrowck<'tcx>(
tcx: TyCtxt<'tcx>,
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
def: LocalDefId,
consumer_options: Option<ConsumerOptions>,
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
) -> (PropagatedBorrowCheckResults<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
let tcx = root_cx.tcx;
let infcx = BorrowckInferCtxt::new(tcx, def);
let (input_body, promoted) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();
let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
if let Some(e) = input_body.tainted_by_errors {
infcx.set_tainted_by_errors(e);
root_cx.set_tainted_by_errors(e);
}
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
@ -185,13 +347,13 @@ fn do_mir_borrowck<'tcx>(
// Compute non-lexical lifetimes.
let nll::NllOutput {
regioncx,
concrete_opaque_types,
polonius_input,
polonius_output,
opt_closure_req,
nll_errors,
polonius_diagnostics,
} = nll::compute_regions(
root_cx,
&infcx,
free_regions,
body,
@ -210,26 +372,19 @@ fn do_mir_borrowck<'tcx>(
// We also have a `#[rustc_regions]` annotation that causes us to dump
// information.
let diags_buffer = &mut BorrowckDiagnosticsBuffer::default();
nll::dump_annotation(
&infcx,
body,
&regioncx,
&opt_closure_req,
&concrete_opaque_types,
diags_buffer,
);
nll::dump_annotation(&infcx, body, &regioncx, &opt_closure_req, diags_buffer);
let movable_coroutine =
// The first argument is the coroutine type passed by value
if let Some(local) = body.local_decls.raw.get(1)
// Get the interior types and args which typeck computed
&& let ty::Coroutine(def_id, _) = *local.ty.kind()
&& tcx.coroutine_movability(def_id) == hir::Movability::Movable
{
true
} else {
false
};
// The first argument is the coroutine type passed by value
if let Some(local) = body.local_decls.raw.get(1)
// Get the interior types and args which typeck computed
&& let ty::Coroutine(def_id, _) = *local.ty.kind()
&& tcx.coroutine_movability(def_id) == hir::Movability::Movable
{
true
} else {
false
};
// While promoteds should mostly be correct by construction, we need to check them for
// invalid moves to detect moving out of arrays:`struct S; fn main() { &([S][0]); }`.
@ -240,6 +395,7 @@ fn do_mir_borrowck<'tcx>(
// this check out of `MirBorrowckCtxt`, actually doing so is far from trivial.
let move_data = MoveData::gather_moves(promoted_body, tcx, |_| true);
let mut promoted_mbcx = MirBorrowckCtxt {
root_cx,
infcx: &infcx,
body: promoted_body,
move_data: &move_data,
@ -280,6 +436,7 @@ fn do_mir_borrowck<'tcx>(
}
let mut mbcx = MirBorrowckCtxt {
root_cx,
infcx: &infcx,
body,
move_data: &move_data,
@ -347,13 +504,13 @@ fn do_mir_borrowck<'tcx>(
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
mbcx.lint_unused_mut();
let tainted_by_errors = mbcx.emit_errors();
if let Some(guar) = mbcx.emit_errors() {
mbcx.root_cx.set_tainted_by_errors(guar);
}
let result = BorrowCheckResult {
concrete_opaque_types: concrete_opaque_types.into_inner(),
let result = PropagatedBorrowCheckResults {
closure_requirements: opt_closure_req,
used_mut_upvars: mbcx.used_mut_upvars,
tainted_by_errors,
};
let body_with_facts = if consumer_options.is_some() {
@ -488,6 +645,7 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
}
struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
root_cx: &'a mut BorrowCheckRootCtxt<'tcx>,
infcx: &'infcx BorrowckInferCtxt<'tcx>,
body: &'a Body<'tcx>,
move_data: &'a MoveData<'tcx>,
@ -1361,11 +1519,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
| AggregateKind::CoroutineClosure(def_id, _)
| AggregateKind::Coroutine(def_id, _) => {
let def_id = def_id.expect_local();
let BorrowCheckResult { used_mut_upvars, .. } =
self.infcx.tcx.mir_borrowck(def_id);
let used_mut_upvars = self.root_cx.used_mut_upvars(def_id);
debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
for field in used_mut_upvars {
self.propagate_closure_used_mut_upvar(&operands[*field]);
// FIXME: We're cloning the `SmallVec` here to avoid borrowing `root_cx`
// when calling `propagate_closure_used_mut_upvar`. This should ideally
// be unnecessary.
for field in used_mut_upvars.clone() {
self.propagate_closure_used_mut_upvar(&operands[field]);
}
}
AggregateKind::Adt(..)

View file

@ -8,10 +8,7 @@ use std::str::FromStr;
use polonius_engine::{Algorithm, Output};
use rustc_index::IndexSlice;
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
use rustc_middle::mir::{
Body, ClosureOutlivesSubject, ClosureRegionRequirements, PassWhere, Promoted, create_dump_file,
dump_enabled, dump_mir,
};
use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TyCtxt};
use rustc_mir_dataflow::ResultsCursor;
@ -25,7 +22,6 @@ use tracing::{debug, instrument};
use crate::borrow_set::BorrowSet;
use crate::consumers::ConsumerOptions;
use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::polonius::PoloniusDiagnosticsContext;
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
@ -33,13 +29,15 @@ use crate::polonius::legacy::{
use crate::region_infer::RegionInferenceContext;
use crate::type_check::{self, MirTypeckResults};
use crate::universal_regions::UniversalRegions;
use crate::{BorrowckInferCtxt, polonius, renumber};
use crate::{
BorrowCheckRootCtxt, BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements,
polonius, renumber,
};
/// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
/// closure requirements to propagate, and any generated errors.
pub(crate) struct NllOutput<'tcx> {
pub regioncx: RegionInferenceContext<'tcx>,
pub concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
pub polonius_input: Option<Box<PoloniusFacts>>,
pub polonius_output: Option<Box<PoloniusOutput>>,
pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
@ -78,6 +76,7 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
///
/// This may result in errors being reported.
pub(crate) fn compute_regions<'a, 'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
universal_regions: UniversalRegions<'tcx>,
body: &Body<'tcx>,
@ -98,8 +97,6 @@ pub(crate) fn compute_regions<'a, 'tcx>(
let location_map = Rc::new(DenseLocationMap::new(body));
let mut concrete_opaque_types = ConcreteOpaqueTypes::default();
// Run the MIR type-checker.
let MirTypeckResults {
constraints,
@ -107,6 +104,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
opaque_type_values,
polonius_context,
} = type_check::type_check(
root_cx,
infcx,
body,
promoted,
@ -117,7 +115,6 @@ pub(crate) fn compute_regions<'a, 'tcx>(
flow_inits,
move_data,
Rc::clone(&location_map),
&mut concrete_opaque_types,
);
// Create the region inference context, taking ownership of the
@ -181,11 +178,10 @@ pub(crate) fn compute_regions<'a, 'tcx>(
infcx.set_tainted_by_errors(guar);
}
regioncx.infer_opaque_types(infcx, opaque_type_values, &mut concrete_opaque_types);
regioncx.infer_opaque_types(root_cx, infcx, opaque_type_values);
NllOutput {
regioncx,
concrete_opaque_types,
polonius_input: polonius_facts.map(Box::new),
polonius_output,
opt_closure_req: closure_region_requirements,
@ -301,7 +297,6 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>,
diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
) {
let tcx = infcx.tcx;
@ -318,7 +313,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
// better.
let def_span = tcx.def_span(body.source.def_id());
let mut err = if let Some(closure_region_requirements) = closure_region_requirements {
let err = if let Some(closure_region_requirements) = closure_region_requirements {
let mut err = infcx.dcx().struct_span_note(def_span, "external requirements");
regioncx.annotate(tcx, &mut err);
@ -344,9 +339,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
err
};
if !concrete_opaque_types.is_empty() {
err.note(format!("Inferred opaque type values:\n{concrete_opaque_types:#?}"));
}
// FIXME(@lcnr): We currently don't dump the inferred hidden types here.
diagnostics_buffer.buffer_non_error(err);
}

View file

@ -1,55 +0,0 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt};
#[derive(Debug, Default)]
pub(super) struct ConcreteOpaqueTypes<'tcx> {
concrete_opaque_types: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
}
impl<'tcx> ConcreteOpaqueTypes<'tcx> {
pub(super) fn is_empty(&self) -> bool {
self.concrete_opaque_types.is_empty()
}
pub(super) fn into_inner(self) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
self.concrete_opaque_types
}
/// Insert an opaque type into the list of opaque types defined by this function
/// after mapping the hidden type to the generic parameters of the opaque type
/// definition.
pub(super) fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
hidden_ty: OpaqueHiddenType<'tcx>,
) {
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
// `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
// only know that once we convert the generic parameters to those of the opaque type.
if let Some(prev) = self.concrete_opaque_types.get_mut(&def_id) {
if prev.ty != hidden_ty.ty {
let (Ok(guar) | Err(guar)) =
prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit());
prev.ty = Ty::new_error(tcx, guar);
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(hidden_ty.span);
} else {
self.concrete_opaque_types.insert(def_id, hidden_ty);
}
}
pub(super) fn extend_from_nested_body(
&mut self,
tcx: TyCtxt<'tcx>,
nested_body: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
) {
for (&def_id, &hidden_ty) in nested_body {
self.insert(tcx, def_id, hidden_ty);
}
}
}

View file

@ -5,7 +5,7 @@ use rustc_index::IndexVec;
use rustc_middle::mir::pretty::{
PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
};
use rustc_middle::mir::{Body, ClosureRegionRequirements, Location};
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::points::PointIndex;
use rustc_session::config::MirIncludeSpans;
@ -17,7 +17,7 @@ use crate::polonius::{
};
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
use crate::{BorrowckInferCtxt, RegionInferenceContext};
use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext};
/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
pub(crate) fn dump_polonius_mir<'tcx>(
@ -334,7 +334,7 @@ fn emit_mermaid_nll_regions<'tcx>(
writeln!(out, "flowchart TD")?;
// Emit the region nodes.
for region in regioncx.var_infos.indices() {
for region in regioncx.definitions.indices() {
write!(out, "{}[\"", region.as_usize())?;
render_region(region, regioncx, out)?;
writeln!(out, "\"]")?;
@ -387,7 +387,7 @@ fn emit_mermaid_nll_sccs<'tcx>(
// Gather and emit the SCC nodes.
let mut nodes_per_scc: IndexVec<_, _> =
regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect();
for region in regioncx.var_infos.indices() {
for region in regioncx.definitions.indices() {
let scc = regioncx.constraint_sccs().scc(region);
nodes_per_scc[scc].push(region);
}

View file

@ -13,18 +13,16 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound,
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
use rustc_middle::bug;
use rustc_middle::mir::{
AnnotationSource, BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject,
ClosureOutlivesSubjectTy, ClosureRegionRequirements, ConstraintCategory, Local, Location,
ReturnConstraint, TerminatorKind,
AnnotationSource, BasicBlock, Body, ConstraintCategory, Local, Location, ReturnConstraint,
TerminatorKind,
};
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex, fold_regions};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::{DUMMY_SP, Span};
use tracing::{debug, instrument, trace};
use tracing::{Level, debug, enabled, instrument, trace};
use crate::BorrowckInferCtxt;
use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
use crate::dataflow::BorrowIndex;
@ -37,6 +35,10 @@ use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, T
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::type_check::{Locations, MirTypeckRegionConstraints};
use crate::universal_regions::UniversalRegions;
use crate::{
BorrowckInferCtxt, ClosureOutlivesRequirement, ClosureOutlivesSubject,
ClosureOutlivesSubjectTy, ClosureRegionRequirements,
};
mod dump_mir;
mod graphviz;
@ -139,13 +141,11 @@ impl RegionTracker {
}
pub struct RegionInferenceContext<'tcx> {
pub var_infos: VarInfos,
/// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionVid`). The
/// definition contains information about where the region came
/// from as well as its final inferred value.
definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
pub(crate) definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
/// The liveness constraints added to each region. For most
/// regions, these start out empty and steadily grow, though for
@ -327,11 +327,13 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
let mut var_to_origin_sorted = var_to_origin.clone().into_iter().collect::<Vec<_>>();
var_to_origin_sorted.sort_by_key(|vto| vto.0);
let mut reg_vars_to_origins_str = "region variables to origins:\n".to_string();
for (reg_var, origin) in var_to_origin_sorted.into_iter() {
reg_vars_to_origins_str.push_str(&format!("{reg_var:?}: {origin:?}\n"));
if enabled!(Level::DEBUG) {
let mut reg_vars_to_origins_str = "region variables to origins:\n".to_string();
for (reg_var, origin) in var_to_origin_sorted.into_iter() {
reg_vars_to_origins_str.push_str(&format!("{reg_var:?}: {origin:?}\n"));
}
debug!("{}", reg_vars_to_origins_str);
}
debug!("{}", reg_vars_to_origins_str);
let num_components = sccs.num_sccs();
let mut components = vec![FxIndexSet::default(); num_components];
@ -342,16 +344,18 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
components[scc_idx.as_usize()].insert((reg_var, *origin));
}
let mut components_str = "strongly connected components:".to_string();
for (scc_idx, reg_vars_origins) in components.iter().enumerate() {
let regions_info = reg_vars_origins.clone().into_iter().collect::<Vec<_>>();
components_str.push_str(&format!(
"{:?}: {:?},\n)",
ConstraintSccIndex::from_usize(scc_idx),
regions_info,
))
if enabled!(Level::DEBUG) {
let mut components_str = "strongly connected components:".to_string();
for (scc_idx, reg_vars_origins) in components.iter().enumerate() {
let regions_info = reg_vars_origins.clone().into_iter().collect::<Vec<_>>();
components_str.push_str(&format!(
"{:?}: {:?},\n)",
ConstraintSccIndex::from_usize(scc_idx),
regions_info,
))
}
debug!("{}", components_str);
}
debug!("{}", components_str);
// calculate the best representative for each component
let components_representatives = components
@ -449,7 +453,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r)));
let mut result = Self {
var_infos,
definitions,
liveness_constraints,
constraints,

View file

@ -10,7 +10,7 @@ use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
use tracing::{debug, instrument};
use super::RegionInferenceContext;
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::BorrowCheckRootCtxt;
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
use crate::universal_regions::RegionClassification;
@ -58,12 +58,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
///
/// [rustc-dev-guide chapter]:
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
#[instrument(level = "debug", skip(self, infcx), ret)]
#[instrument(level = "debug", skip(self, root_cx, infcx), ret)]
pub(crate) fn infer_opaque_types(
&self,
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &InferCtxt<'tcx>,
opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
) {
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
FxIndexMap::default();
@ -140,11 +140,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
concrete_opaque_types.insert(
infcx.tcx,
root_cx.add_concrete_opaque_type(
opaque_type_key.def_id,
OpaqueHiddenType { ty, span: concrete_type.span },
OpaqueHiddenType { span: concrete_type.span, ty },
);
// Check that all opaque types have the same region parameters if they have the same
// non-region parameters. This is necessary because within the new solver we perform
// various query operations modulo regions, and thus could unsoundly select some impls
@ -186,7 +186,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
fold_regions(tcx, ty, |region, _| match *region {
fold_regions(tcx, ty, |region, _| match region.kind() {
ty::ReVar(vid) => {
let scc = self.constraint_sccs.scc(vid);

View file

@ -0,0 +1,101 @@
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::bug;
use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use smallvec::SmallVec;
use crate::{ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults};
/// The shared context used by both the root as well as all its nested
/// items.
pub(super) struct BorrowCheckRootCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,
root_def_id: LocalDefId,
concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
nested_bodies: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
tainted_by_errors: Option<ErrorGuaranteed>,
}
impl<'tcx> BorrowCheckRootCtxt<'tcx> {
pub(super) fn new(tcx: TyCtxt<'tcx>, root_def_id: LocalDefId) -> BorrowCheckRootCtxt<'tcx> {
BorrowCheckRootCtxt {
tcx,
root_def_id,
concrete_opaque_types: Default::default(),
nested_bodies: Default::default(),
tainted_by_errors: None,
}
}
/// Collect all defining uses of opaque types inside of this typeck root. This
/// expects the hidden type to be mapped to the definition parameters of the opaque
/// and errors if we end up with distinct hidden types.
pub(super) fn add_concrete_opaque_type(
&mut self,
def_id: LocalDefId,
hidden_ty: OpaqueHiddenType<'tcx>,
) {
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
// `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
// only know that once we convert the generic parameters to those of the opaque type.
if let Some(prev) = self.concrete_opaque_types.0.get_mut(&def_id) {
if prev.ty != hidden_ty.ty {
let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| {
let (Ok(e) | Err(e)) =
prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit());
e
});
prev.ty = Ty::new_error(self.tcx, guar);
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(hidden_ty.span);
} else {
self.concrete_opaque_types.0.insert(def_id, hidden_ty);
}
}
pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) {
self.tainted_by_errors = Some(guar);
}
fn get_or_insert_nested(&mut self, def_id: LocalDefId) -> &PropagatedBorrowCheckResults<'tcx> {
debug_assert_eq!(
self.tcx.typeck_root_def_id(def_id.to_def_id()),
self.root_def_id.to_def_id()
);
if !self.nested_bodies.contains_key(&def_id) {
let result = super::do_mir_borrowck(self, def_id, None).0;
if let Some(prev) = self.nested_bodies.insert(def_id, result) {
bug!("unexpected previous nested body: {prev:?}");
}
}
self.nested_bodies.get(&def_id).unwrap()
}
pub(super) fn closure_requirements(
&mut self,
nested_body_def_id: LocalDefId,
) -> &Option<ClosureRegionRequirements<'tcx>> {
&self.get_or_insert_nested(nested_body_def_id).closure_requirements
}
pub(super) fn used_mut_upvars(
&mut self,
nested_body_def_id: LocalDefId,
) -> &SmallVec<[FieldIdx; 8]> {
&self.get_or_insert_nested(nested_body_def_id).used_mut_upvars
}
pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> {
if let Some(guar) = self.tainted_by_errors {
Err(guar)
} else {
Ok(self.tcx.arena.alloc(self.concrete_opaque_types))
}
}
}

View file

@ -6,7 +6,6 @@ use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
use rustc_infer::traits::query::type_op::DeeplyNormalize;
use rustc_middle::bug;
use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory};
use rustc_middle::ty::{
self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions,
};
@ -18,6 +17,7 @@ use crate::constraints::OutlivesConstraint;
use crate::region_infer::TypeTest;
use crate::type_check::{Locations, MirTypeckRegionConstraints};
use crate::universal_regions::UniversalRegions;
use crate::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory};
pub(crate) struct ConstraintConversion<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
@ -205,7 +205,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
/// are dealt with during trait solving.
fn replace_placeholders_with_nll<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T {
if value.has_placeholders() {
fold_regions(self.tcx, value, |r, _| match *r {
fold_regions(self.tcx, value, |r, _| match r.kind() {
ty::RePlaceholder(placeholder) => {
self.constraints.placeholder_region(self.infcx, placeholder)
}
@ -227,7 +227,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
}
fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> ty::RegionVid {
if let ty::RePlaceholder(placeholder) = *r {
if let ty::RePlaceholder(placeholder) = r.kind() {
self.constraints.placeholder_region(self.infcx, placeholder).as_var()
} else {
self.universal_regions.to_region_vid(r)

View file

@ -45,7 +45,6 @@ use crate::borrow_set::BorrowSet;
use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet};
use crate::diagnostics::UniverseInfo;
use crate::member_constraints::MemberConstraintSet;
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable};
use crate::polonius::{PoloniusContext, PoloniusLivenessContext};
use crate::region_infer::TypeTest;
@ -53,7 +52,7 @@ use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderI
use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst};
use crate::type_check::free_region_relations::{CreateResult, UniversalRegionRelations};
use crate::universal_regions::{DefiningTy, UniversalRegions};
use crate::{BorrowckInferCtxt, path_utils};
use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, path_utils};
macro_rules! span_mirbug {
($context:expr, $elem:expr, $($message:tt)*) => ({
@ -102,6 +101,7 @@ mod relate_tys;
/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
/// - `location_map` -- map between MIR `Location` and `PointIndex`
pub(crate) fn type_check<'a, 'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
promoted: &IndexSlice<Promoted, Body<'tcx>>,
@ -112,7 +112,6 @@ pub(crate) fn type_check<'a, 'tcx>(
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
move_data: &MoveData<'tcx>,
location_map: Rc<DenseLocationMap>,
concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
) -> MirTypeckResults<'tcx> {
let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body);
let mut constraints = MirTypeckRegionConstraints {
@ -153,6 +152,7 @@ pub(crate) fn type_check<'a, 'tcx>(
};
let mut typeck = TypeChecker {
root_cx,
infcx,
last_span: body.span,
body,
@ -167,7 +167,6 @@ pub(crate) fn type_check<'a, 'tcx>(
polonius_facts,
borrow_set,
constraints: &mut constraints,
concrete_opaque_types,
polonius_liveness,
};
@ -215,6 +214,7 @@ enum FieldAccessError {
/// way, it accrues region constraints -- these can later be used by
/// NLL region checking.
struct TypeChecker<'a, 'tcx> {
root_cx: &'a mut BorrowCheckRootCtxt<'tcx>,
infcx: &'a BorrowckInferCtxt<'tcx>,
last_span: Span,
body: &'a Body<'tcx>,
@ -233,7 +233,6 @@ struct TypeChecker<'a, 'tcx> {
polonius_facts: &'a mut Option<PoloniusFacts>,
borrow_set: &'a BorrowSet<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
concrete_opaque_types: &'a mut ConcreteOpaqueTypes<'tcx>,
/// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints.
polonius_liveness: Option<PoloniusLivenessContext>,
}
@ -2503,11 +2502,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
args: GenericArgsRef<'tcx>,
locations: Locations,
) -> ty::InstantiatedPredicates<'tcx> {
let closure_borrowck_results = tcx.mir_borrowck(def_id);
self.concrete_opaque_types
.extend_from_nested_body(tcx, &closure_borrowck_results.concrete_opaque_types);
if let Some(closure_requirements) = &closure_borrowck_results.closure_requirements {
if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) {
constraint_conversion::ConstraintConversion::new(
self.infcx,
self.universal_regions,

View file

@ -271,7 +271,7 @@ where
}
fn visit_region(&mut self, r: ty::Region<'tcx>) {
match *r {
match r.kind() {
// ignore bound regions, keep visiting
ty::ReBound(_, _) => {}
_ => (self.op)(r),

View file

@ -909,19 +909,19 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
/// if it is a placeholder. Handling placeholders requires access to the
/// `MirTypeckRegionConstraints`.
fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
if let ty::ReVar(..) = *r {
r.as_var()
} else if let ty::ReError(guar) = *r {
self.tainted_by_errors.set(Some(guar));
// We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the
// `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if
// errors are being emitted and 2) it leaves the happy path unaffected.
self.fr_static
} else {
*self
match r.kind() {
ty::ReVar(..) => r.as_var(),
ty::ReError(guar) => {
self.tainted_by_errors.set(Some(guar));
// We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the
// `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if
// errors are being emitted and 2) it leaves the happy path unaffected.
self.fr_static
}
_ => *self
.indices
.get(&r)
.unwrap_or_else(|| bug!("cannot convert `{:?}` to a region vid", r))
.unwrap_or_else(|| bug!("cannot convert `{:?}` to a region vid", r)),
}
}

View file

@ -17,7 +17,7 @@ mod llvm_enzyme {
use rustc_ast::visit::AssocCtxt::*;
use rustc_ast::{
self as ast, AssocItemKind, BindingMode, ExprKind, FnRetTy, FnSig, Generics, ItemKind,
MetaItemInner, PatKind, QSelf, TyKind,
MetaItemInner, PatKind, QSelf, TyKind, Visibility,
};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Ident, Span, Symbol, kw, sym};
@ -72,6 +72,16 @@ mod llvm_enzyme {
}
}
// Get information about the function the macro is applied to
fn extract_item_info(iitem: &P<ast::Item>) -> Option<(Visibility, FnSig, Ident)> {
match &iitem.kind {
ItemKind::Fn(box ast::Fn { sig, ident, .. }) => {
Some((iitem.vis.clone(), sig.clone(), ident.clone()))
}
_ => None,
}
}
pub(crate) fn from_ast(
ecx: &mut ExtCtxt<'_>,
meta_item: &ThinVec<MetaItemInner>,
@ -199,32 +209,26 @@ mod llvm_enzyme {
return vec![item];
}
let dcx = ecx.sess.dcx();
// first get the annotable item:
let (primal, sig, is_impl): (Ident, FnSig, bool) = match &item {
Annotatable::Item(iitem) => {
let (ident, sig) = match &iitem.kind {
ItemKind::Fn(box ast::Fn { ident, sig, .. }) => (ident, sig),
_ => {
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
return vec![item];
}
};
(*ident, sig.clone(), false)
}
// first get information about the annotable item:
let Some((vis, sig, primal)) = (match &item {
Annotatable::Item(iitem) => extract_item_info(iitem),
Annotatable::Stmt(stmt) => match &stmt.kind {
ast::StmtKind::Item(iitem) => extract_item_info(iitem),
_ => None,
},
Annotatable::AssocItem(assoc_item, Impl { of_trait: false }) => {
let (ident, sig) = match &assoc_item.kind {
ast::AssocItemKind::Fn(box ast::Fn { ident, sig, .. }) => (ident, sig),
_ => {
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
return vec![item];
match &assoc_item.kind {
ast::AssocItemKind::Fn(box ast::Fn { sig, ident, .. }) => {
Some((assoc_item.vis.clone(), sig.clone(), ident.clone()))
}
};
(*ident, sig.clone(), true)
}
_ => {
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
return vec![item];
_ => None,
}
}
_ => None,
}) else {
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
return vec![item];
};
let meta_item_vec: ThinVec<MetaItemInner> = match meta_item.kind {
@ -238,15 +242,6 @@ mod llvm_enzyme {
let has_ret = has_ret(&sig.decl.output);
let sig_span = ecx.with_call_site_ctxt(sig.span);
let vis = match &item {
Annotatable::Item(iitem) => iitem.vis.clone(),
Annotatable::AssocItem(assoc_item, _) => assoc_item.vis.clone(),
_ => {
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
return vec![item];
}
};
// create TokenStream from vec elemtents:
// meta_item doesn't have a .tokens field
let mut ts: Vec<TokenTree> = vec![];
@ -379,6 +374,22 @@ mod llvm_enzyme {
}
Annotatable::AssocItem(assoc_item.clone(), i)
}
Annotatable::Stmt(ref mut stmt) => {
match stmt.kind {
ast::StmtKind::Item(ref mut iitem) => {
if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) {
iitem.attrs.push(attr);
}
if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind))
{
iitem.attrs.push(inline_never.clone());
}
}
_ => unreachable!("stmt kind checked previously"),
};
Annotatable::Stmt(stmt.clone())
}
_ => {
unreachable!("annotatable kind checked previously")
}
@ -389,22 +400,40 @@ mod llvm_enzyme {
delim: rustc_ast::token::Delimiter::Parenthesis,
tokens: ts,
});
let d_attr = outer_normal_attr(&rustc_ad_attr, new_id, span);
let d_annotatable = if is_impl {
let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf);
let d_fn = P(ast::AssocItem {
attrs: thin_vec![d_attr, inline_never],
id: ast::DUMMY_NODE_ID,
span,
vis,
kind: assoc_item,
tokens: None,
});
Annotatable::AssocItem(d_fn, Impl { of_trait: false })
} else {
let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
d_fn.vis = vis;
Annotatable::Item(d_fn)
let d_annotatable = match &item {
Annotatable::AssocItem(_, _) => {
let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf);
let d_fn = P(ast::AssocItem {
attrs: thin_vec![d_attr, inline_never],
id: ast::DUMMY_NODE_ID,
span,
vis,
kind: assoc_item,
tokens: None,
});
Annotatable::AssocItem(d_fn, Impl { of_trait: false })
}
Annotatable::Item(_) => {
let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
d_fn.vis = vis;
Annotatable::Item(d_fn)
}
Annotatable::Stmt(_) => {
let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
d_fn.vis = vis;
Annotatable::Stmt(P(ast::Stmt {
id: ast::DUMMY_NODE_ID,
kind: ast::StmtKind::Item(d_fn),
span,
}))
}
_ => {
unreachable!("item kind checked previously")
}
};
return vec![orig_annotatable, d_annotatable];

View file

@ -1,5 +1,4 @@
//! Codegen of intrinsics. This includes `extern "rust-intrinsic"`,
//! functions marked with the `#[rustc_intrinsic]` attribute
//! Codegen of intrinsics. This includes functions marked with the `#[rustc_intrinsic]` attribute
//! and LLVM intrinsics that have symbol names starting with `llvm.`.
macro_rules! intrinsic_args {

View file

@ -439,12 +439,9 @@ fn report_inline_asm(
let span = if cookie == 0 || matches!(cgcx.lto, Lto::Fat | Lto::Thin) {
SpanData::default()
} else {
let lo = BytePos::from_u32(cookie as u32);
let hi = BytePos::from_u32((cookie >> 32) as u32);
SpanData {
lo,
// LLVM version < 19 silently truncates the cookie to 32 bits in some situations.
hi: if hi.to_u32() != 0 { hi } else { lo },
lo: BytePos::from_u32(cookie as u32),
hi: BytePos::from_u32((cookie >> 32) as u32),
ctxt: SyntaxContext::root(),
parent: None,
}

View file

@ -201,7 +201,23 @@ fn compute_enzyme_fn_ty<'ll>(
}
if attrs.width == 1 {
todo!("Handle sret for scalar ad");
// Enzyme returns a struct of style:
// `{ original_ret(if requested), float, float, ... }`
let mut struct_elements = vec![];
if attrs.has_primal_ret() {
struct_elements.push(inner_ret_ty);
}
// Next, we push the list of active floats, since they will be lowered to `enzyme_out`,
// and therefore part of the return struct.
let param_tys = cx.func_params_types(fn_ty);
for (act, param_ty) in attrs.input_activity.iter().zip(param_tys) {
if matches!(act, DiffActivity::Active) {
// Now find the float type at position i based on the fn_ty,
// to know what (f16/f32/f64/...) to add to the struct.
struct_elements.push(param_ty);
}
}
ret_ty = cx.type_struct(&struct_elements, false);
} else {
// First we check if we also have to deal with the primal return.
match attrs.mode {
@ -388,7 +404,11 @@ fn generate_enzyme_call<'ll>(
// now store the result of the enzyme call into the sret pointer.
let sret_ptr = outer_args[0];
let call_ty = cx.val_ty(call);
assert_eq!(cx.type_kind(call_ty), TypeKind::Array);
if attrs.width == 1 {
assert_eq!(cx.type_kind(call_ty), TypeKind::Struct);
} else {
assert_eq!(cx.type_kind(call_ty), TypeKind::Array);
}
llvm::LLVMBuildStore(&builder.llbuilder, call, sret_ptr);
}
builder.ret_void();

View file

@ -5,15 +5,11 @@ use rustc_abi::Align;
use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_data_structures::fx::FxIndexMap;
use rustc_index::IndexVec;
use rustc_middle::mir;
use rustc_middle::mir::mono::MonoItemPartitions;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::TyCtxt;
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::def_id::DefIdSet;
use rustc_span::{SourceFile, StableSourceFileId};
use tracing::debug;
@ -24,6 +20,7 @@ use crate::llvm;
mod covfun;
mod spans;
mod unused;
/// Generates and exports the coverage map, which is embedded in special
/// linker sections in the final binary.
@ -56,13 +53,6 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
None => return,
};
// The order of entries in this global file table needs to be deterministic,
// and ideally should also be independent of the details of stable-hashing,
// because coverage tests snapshots (`.cov-map`) can observe the order and
// would need to be re-blessed if it changes. As long as those requirements
// are satisfied, the order can be arbitrary.
let mut global_file_table = GlobalFileTable::new();
let mut covfun_records = instances_used
.iter()
.copied()
@ -70,18 +60,13 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
// order that doesn't depend on the stable-hash-based order in which
// instances were visited during codegen.
.sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name)
.filter_map(|instance| prepare_covfun_record(tcx, &mut global_file_table, instance, true))
.filter_map(|instance| prepare_covfun_record(tcx, instance, true))
.collect::<Vec<_>>();
// In a single designated CGU, also prepare covfun records for functions
// in this crate that were instrumented for coverage, but are unused.
if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
let mut unused_instances = gather_unused_function_instances(cx);
// Sort the unused instances by symbol name, for the same reason as the used ones.
unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name);
covfun_records.extend(unused_instances.into_iter().filter_map(|instance| {
prepare_covfun_record(tcx, &mut global_file_table, instance, false)
}));
unused::prepare_covfun_records_for_unused_functions(cx, &mut covfun_records);
}
// If there are no covfun records for this CGU, don't generate a covmap record.
@ -93,91 +78,88 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
return;
}
// Encode all filenames referenced by coverage mappings in this CGU.
let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
// The `llvm-cov` tool uses this hash to associate each covfun record with
// its corresponding filenames table, since the final binary will typically
// contain multiple covmap records from different compilation units.
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
let mut unused_function_names = vec![];
// Prepare the global file table for this CGU, containing all paths needed
// by one or more covfun records.
let global_file_table =
GlobalFileTable::build(tcx, covfun_records.iter().flat_map(|c| c.all_source_files()));
for covfun in &covfun_records {
unused_function_names.extend(covfun.mangled_function_name_if_unused());
covfun::generate_covfun_record(cx, filenames_hash, covfun)
}
// For unused functions, we need to take their mangled names and store them
// in a specially-named global array. LLVM's `InstrProfiling` pass will
// detect this global and include those names in its `__llvm_prf_names`
// section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
if !unused_function_names.is_empty() {
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
let name_globals = unused_function_names
.into_iter()
.map(|mangled_function_name| cx.const_str(mangled_function_name).0)
.collect::<Vec<_>>();
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names");
llvm::set_global_constant(array, true);
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
llvm::set_initializer(array, initializer);
covfun::generate_covfun_record(cx, &global_file_table, covfun)
}
// Generate the coverage map header, which contains the filenames used by
// this CGU's coverage mappings, and store it in a well-known global.
// (This is skipped if we returned early due to having no covfun records.)
generate_covmap_record(cx, covmap_version, &filenames_buffer);
generate_covmap_record(cx, covmap_version, &global_file_table.filenames_buffer);
}
/// Maps "global" (per-CGU) file ID numbers to their underlying source files.
/// Maps "global" (per-CGU) file ID numbers to their underlying source file paths.
#[derive(Debug)]
struct GlobalFileTable {
/// This "raw" table doesn't include the working dir, so a file's
/// global ID is its index in this set **plus one**.
raw_file_table: FxIndexMap<StableSourceFileId, Arc<SourceFile>>,
raw_file_table: FxIndexMap<StableSourceFileId, String>,
/// The file table in encoded form (possibly compressed), which can be
/// included directly in this CGU's `__llvm_covmap` record.
filenames_buffer: Vec<u8>,
/// Truncated hash of the bytes in `filenames_buffer`.
///
/// The `llvm-cov` tool uses this hash to associate each covfun record with
/// its corresponding filenames table, since the final binary will typically
/// contain multiple covmap records from different compilation units.
filenames_hash: u64,
}
impl GlobalFileTable {
fn new() -> Self {
Self { raw_file_table: FxIndexMap::default() }
}
/// Builds a "global file table" for this CGU, mapping numeric IDs to
/// path strings.
fn build<'a>(tcx: TyCtxt<'_>, all_files: impl Iterator<Item = &'a SourceFile>) -> Self {
let mut raw_file_table = FxIndexMap::default();
fn global_file_id_for_file(&mut self, file: &Arc<SourceFile>) -> GlobalFileId {
// Ensure the given file has a table entry, and get its index.
let entry = self.raw_file_table.entry(file.stable_id);
let raw_id = entry.index();
entry.or_insert_with(|| Arc::clone(file));
for file in all_files {
raw_file_table.entry(file.stable_id).or_insert_with(|| {
file.name
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy()
.into_owned()
});
}
// The raw file table doesn't include an entry for the working dir
// (which has ID 0), so add 1 to get the correct ID.
GlobalFileId::from_usize(raw_id + 1)
}
// FIXME(Zalathar): Consider sorting the file table here, but maybe
// only after adding filename support to coverage-dump, so that the
// table order isn't directly visible in `.coverage-map` snapshots.
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
let mut table = Vec::with_capacity(self.raw_file_table.len() + 1);
let mut table = Vec::with_capacity(raw_file_table.len() + 1);
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
// requires setting the first filename to the compilation directory.
// Since rustc generates coverage maps with relative paths, the
// compilation directory can be combined with the relative paths
// to get absolute paths, if needed.
table.push(
tcx.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy(),
);
// Since version 6 of the LLVM coverage mapping format, the first entry
// in the global file table is treated as a base directory, used to
// resolve any other entries that are stored as relative paths.
let base_dir = tcx
.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy();
table.push(base_dir.as_ref());
// Add the regular entries after the base directory.
table.extend(self.raw_file_table.values().map(|file| {
file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy()
}));
table.extend(raw_file_table.values().map(|name| name.as_str()));
llvm_cov::write_filenames_to_buffer(&table)
// Encode the file table into a buffer, and get the hash of its encoded
// bytes, so that we can embed that hash in `__llvm_covfun` records.
let filenames_buffer = llvm_cov::write_filenames_to_buffer(&table);
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
Self { raw_file_table, filenames_buffer, filenames_hash }
}
fn get_existing_id(&self, file: &SourceFile) -> Option<GlobalFileId> {
let raw_id = self.raw_file_table.get_index_of(&file.stable_id)?;
// The raw file table doesn't include an entry for the base dir
// (which has ID 0), so add 1 to get the correct ID.
Some(GlobalFileId::from_usize(raw_id + 1))
}
}
@ -193,26 +175,31 @@ rustc_index::newtype_index! {
struct LocalFileId {}
}
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
/// file IDs.
/// Holds a mapping from "local" (per-function) file IDs to their corresponding
/// source files.
#[derive(Debug, Default)]
struct VirtualFileMapping {
local_to_global: IndexVec<LocalFileId, GlobalFileId>,
global_to_local: FxIndexMap<GlobalFileId, LocalFileId>,
local_file_table: IndexVec<LocalFileId, Arc<SourceFile>>,
}
impl VirtualFileMapping {
fn local_id_for_global(&mut self, global_file_id: GlobalFileId) -> LocalFileId {
*self
.global_to_local
.entry(global_file_id)
.or_insert_with(|| self.local_to_global.push(global_file_id))
fn push_file(&mut self, source_file: &Arc<SourceFile>) -> LocalFileId {
self.local_file_table.push(Arc::clone(source_file))
}
fn to_vec(&self) -> Vec<u32> {
// This clone could be avoided by transmuting `&[GlobalFileId]` to `&[u32]`,
// but it isn't hot or expensive enough to justify the extra unsafety.
self.local_to_global.iter().map(|&global| GlobalFileId::as_u32(global)).collect()
/// Resolves all of the filenames in this local file mapping to a list of
/// global file IDs in its CGU, for inclusion in this function's
/// `__llvm_covfun` record.
///
/// The global file IDs are returned as `u32` to make FFI easier.
fn resolve_all(&self, global_file_table: &GlobalFileTable) -> Option<Vec<u32>> {
self.local_file_table
.iter()
.map(|file| try {
let id = global_file_table.get_existing_id(file)?;
GlobalFileId::as_u32(id)
})
.collect::<Option<Vec<_>>>()
}
}
@ -249,121 +236,3 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
cx.add_used_global(covmap_global);
}
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
/// But since we don't want unused functions to disappear from coverage reports, we also scan for
/// functions that were instrumented but are not participating in codegen.
///
/// These unused functions don't need to be codegenned, but we do need to add them to the function
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
/// We also end up adding their symbol names to a special global array that LLVM will include in
/// its embedded coverage data.
fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec<ty::Instance<'tcx>> {
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
let tcx = cx.tcx;
let usage = prepare_usage_sets(tcx);
let is_unused_fn = |def_id: LocalDefId| -> bool {
// Usage sets expect `DefId`, so convert from `LocalDefId`.
let d: DefId = LocalDefId::to_def_id(def_id);
// To be potentially eligible for "unused function" mappings, a definition must:
// - Be eligible for coverage instrumentation
// - Not participate directly in codegen (or have lost all its coverage statements)
// - Not have any coverage statements inlined into codegenned functions
tcx.is_eligible_for_coverage(def_id)
&& (!usage.all_mono_items.contains(&d) || usage.missing_own_coverage.contains(&d))
&& !usage.used_via_inlining.contains(&d)
};
// FIXME(#79651): Consider trying to filter out dummy instantiations of
// unused generic functions from library crates, because they can produce
// "unused instantiation" in coverage reports even when they are actually
// used by some downstream crate in the same binary.
tcx.mir_keys(())
.iter()
.copied()
.filter(|&def_id| is_unused_fn(def_id))
.map(|def_id| make_dummy_instance(tcx, def_id))
.collect::<Vec<_>>()
}
struct UsageSets<'tcx> {
all_mono_items: &'tcx DefIdSet,
used_via_inlining: FxHashSet<DefId>,
missing_own_coverage: FxHashSet<DefId>,
}
/// Prepare sets of definitions that are relevant to deciding whether something
/// is an "unused function" for coverage purposes.
fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
let MonoItemPartitions { all_mono_items, codegen_units, .. } =
tcx.collect_and_partition_mono_items(());
// Obtain a MIR body for each function participating in codegen, via an
// arbitrary instance.
let mut def_ids_seen = FxHashSet::default();
let def_and_mir_for_all_mono_fns = codegen_units
.iter()
.flat_map(|cgu| cgu.items().keys())
.filter_map(|item| match item {
mir::mono::MonoItem::Fn(instance) => Some(instance),
mir::mono::MonoItem::Static(_) | mir::mono::MonoItem::GlobalAsm(_) => None,
})
// We only need one arbitrary instance per definition.
.filter(move |instance| def_ids_seen.insert(instance.def_id()))
.map(|instance| {
// We don't care about the instance, just its underlying MIR.
let body = tcx.instance_mir(instance.def);
(instance.def_id(), body)
});
// Functions whose coverage statements were found inlined into other functions.
let mut used_via_inlining = FxHashSet::default();
// Functions that were instrumented, but had all of their coverage statements
// removed by later MIR transforms (e.g. UnreachablePropagation).
let mut missing_own_coverage = FxHashSet::default();
for (def_id, body) in def_and_mir_for_all_mono_fns {
let mut saw_own_coverage = false;
// Inspect every coverage statement in the function's MIR.
for stmt in body
.basic_blocks
.iter()
.flat_map(|block| &block.statements)
.filter(|stmt| matches!(stmt.kind, mir::StatementKind::Coverage(_)))
{
if let Some(inlined) = stmt.source_info.scope.inlined_instance(&body.source_scopes) {
// This coverage statement was inlined from another function.
used_via_inlining.insert(inlined.def_id());
} else {
// Non-inlined coverage statements belong to the enclosing function.
saw_own_coverage = true;
}
}
if !saw_own_coverage && body.function_coverage_info.is_some() {
missing_own_coverage.insert(def_id);
}
}
UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
}
fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> {
let def_id = local_def_id.to_def_id();
// Make a dummy instance that fills in all generics with placeholders.
ty::Instance::new(
def_id,
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
if let ty::GenericParamDefKind::Lifetime = param.kind {
tcx.lifetimes.re_erased.into()
} else {
tcx.mk_param_from_def(param)
}
}),
)
}

View file

@ -5,6 +5,7 @@
//! [^win]: On Windows the section name is `.lcovfun`.
use std::ffi::CString;
use std::sync::Arc;
use rustc_abi::Align;
use rustc_codegen_ssa::traits::{
@ -15,7 +16,7 @@ use rustc_middle::mir::coverage::{
MappingKind, Op,
};
use rustc_middle::ty::{Instance, TyCtxt};
use rustc_span::Span;
use rustc_span::{SourceFile, Span};
use rustc_target::spec::HasTargetSpec;
use tracing::debug;
@ -38,16 +39,15 @@ pub(crate) struct CovfunRecord<'tcx> {
}
impl<'tcx> CovfunRecord<'tcx> {
/// FIXME(Zalathar): Make this the responsibility of the code that determines
/// which functions are unused.
pub(crate) fn mangled_function_name_if_unused(&self) -> Option<&'tcx str> {
(!self.is_used).then_some(self.mangled_function_name)
/// Iterator that yields all source files referred to by this function's
/// coverage mappings. Used to build the global file table for the CGU.
pub(crate) fn all_source_files(&self) -> impl Iterator<Item = &SourceFile> {
self.virtual_file_mapping.local_file_table.iter().map(Arc::as_ref)
}
}
pub(crate) fn prepare_covfun_record<'tcx>(
tcx: TyCtxt<'tcx>,
global_file_table: &mut GlobalFileTable,
instance: Instance<'tcx>,
is_used: bool,
) -> Option<CovfunRecord<'tcx>> {
@ -65,7 +65,7 @@ pub(crate) fn prepare_covfun_record<'tcx>(
regions: ffi::Regions::default(),
};
fill_region_tables(tcx, global_file_table, fn_cov_info, ids_info, &mut covfun);
fill_region_tables(tcx, fn_cov_info, ids_info, &mut covfun);
if covfun.regions.has_no_regions() {
debug!(?covfun, "function has no mappings to embed; skipping");
@ -100,7 +100,6 @@ fn prepare_expressions(ids_info: &CoverageIdsInfo) -> Vec<ffi::CounterExpression
/// Populates the mapping region tables in the current function's covfun record.
fn fill_region_tables<'tcx>(
tcx: TyCtxt<'tcx>,
global_file_table: &mut GlobalFileTable,
fn_cov_info: &'tcx FunctionCoverageInfo,
ids_info: &'tcx CoverageIdsInfo,
covfun: &mut CovfunRecord<'tcx>,
@ -114,11 +113,7 @@ fn fill_region_tables<'tcx>(
};
let source_file = source_map.lookup_source_file(first_span.lo());
// Look up the global file ID for that file.
let global_file_id = global_file_table.global_file_id_for_file(&source_file);
// Associate that global file ID with a local file ID for this function.
let local_file_id = covfun.virtual_file_mapping.local_id_for_global(global_file_id);
let local_file_id = covfun.virtual_file_mapping.push_file(&source_file);
// In rare cases, _all_ of a function's spans are discarded, and coverage
// codegen needs to handle that gracefully to avoid #133606.
@ -187,7 +182,7 @@ fn fill_region_tables<'tcx>(
/// as a global variable in the `__llvm_covfun` section.
pub(crate) fn generate_covfun_record<'tcx>(
cx: &CodegenCx<'_, 'tcx>,
filenames_hash: u64,
global_file_table: &GlobalFileTable,
covfun: &CovfunRecord<'tcx>,
) {
let &CovfunRecord {
@ -199,12 +194,19 @@ pub(crate) fn generate_covfun_record<'tcx>(
ref regions,
} = covfun;
let Some(local_file_table) = virtual_file_mapping.resolve_all(global_file_table) else {
debug_assert!(
false,
"all local files should be present in the global file table: \
global_file_table = {global_file_table:?}, \
virtual_file_mapping = {virtual_file_mapping:?}"
);
return;
};
// Encode the function's coverage mappings into a buffer.
let coverage_mapping_buffer = llvm_cov::write_function_mappings_to_buffer(
&virtual_file_mapping.to_vec(),
expressions,
regions,
);
let coverage_mapping_buffer =
llvm_cov::write_function_mappings_to_buffer(&local_file_table, expressions, regions);
// A covfun record consists of four target-endian integers, followed by the
// encoded mapping data in bytes. Note that the length field is 32 bits.
@ -217,7 +219,7 @@ pub(crate) fn generate_covfun_record<'tcx>(
cx.const_u64(func_name_hash),
cx.const_u32(coverage_mapping_buffer.len() as u32),
cx.const_u64(source_hash),
cx.const_u64(filenames_hash),
cx.const_u64(global_file_table.filenames_hash),
cx.const_bytes(&coverage_mapping_buffer),
],
// This struct needs to be packed, so that the 32-bit length field

View file

@ -0,0 +1,170 @@
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::mir;
use rustc_middle::mir::mono::MonoItemPartitions;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::def_id::DefIdSet;
use crate::common::CodegenCx;
use crate::coverageinfo::mapgen::covfun::{CovfunRecord, prepare_covfun_record};
use crate::llvm;
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
/// But since we don't want unused functions to disappear from coverage reports, we also scan for
/// functions that were instrumented but are not participating in codegen.
///
/// These unused functions don't need to be codegenned, but we do need to add them to the function
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
/// We also end up adding their symbol names to a special global array that LLVM will include in
/// its embedded coverage data.
pub(crate) fn prepare_covfun_records_for_unused_functions<'tcx>(
cx: &CodegenCx<'_, 'tcx>,
covfun_records: &mut Vec<CovfunRecord<'tcx>>,
) {
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
let mut unused_instances = gather_unused_function_instances(cx);
// Sort the unused instances by symbol name, so that their order isn't hash-sensitive.
unused_instances.sort_by_key(|instance| instance.symbol_name);
// Try to create a covfun record for each unused function.
let mut name_globals = Vec::with_capacity(unused_instances.len());
covfun_records.extend(unused_instances.into_iter().filter_map(|unused| try {
let record = prepare_covfun_record(cx.tcx, unused.instance, false)?;
// If successful, also store its symbol name in a global constant.
name_globals.push(cx.const_str(unused.symbol_name.name).0);
record
}));
// Store the names of unused functions in a specially-named global array.
// LLVM's `InstrProfilling` pass will detect this array, and include the
// referenced names in its `__llvm_prf_names` section.
// (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
if !name_globals.is_empty() {
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names");
llvm::set_global_constant(array, true);
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
llvm::set_initializer(array, initializer);
}
}
/// Holds a dummy function instance along with its symbol name, to avoid having
/// to repeatedly query for the name.
struct UnusedInstance<'tcx> {
instance: ty::Instance<'tcx>,
symbol_name: ty::SymbolName<'tcx>,
}
fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec<UnusedInstance<'tcx>> {
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
let tcx = cx.tcx;
let usage = prepare_usage_sets(tcx);
let is_unused_fn = |def_id: LocalDefId| -> bool {
// Usage sets expect `DefId`, so convert from `LocalDefId`.
let d: DefId = LocalDefId::to_def_id(def_id);
// To be potentially eligible for "unused function" mappings, a definition must:
// - Be eligible for coverage instrumentation
// - Not participate directly in codegen (or have lost all its coverage statements)
// - Not have any coverage statements inlined into codegenned functions
tcx.is_eligible_for_coverage(def_id)
&& (!usage.all_mono_items.contains(&d) || usage.missing_own_coverage.contains(&d))
&& !usage.used_via_inlining.contains(&d)
};
// FIXME(#79651): Consider trying to filter out dummy instantiations of
// unused generic functions from library crates, because they can produce
// "unused instantiation" in coverage reports even when they are actually
// used by some downstream crate in the same binary.
tcx.mir_keys(())
.iter()
.copied()
.filter(|&def_id| is_unused_fn(def_id))
.map(|def_id| make_dummy_instance(tcx, def_id))
.map(|instance| UnusedInstance { instance, symbol_name: tcx.symbol_name(instance) })
.collect::<Vec<_>>()
}
struct UsageSets<'tcx> {
all_mono_items: &'tcx DefIdSet,
used_via_inlining: FxHashSet<DefId>,
missing_own_coverage: FxHashSet<DefId>,
}
/// Prepare sets of definitions that are relevant to deciding whether something
/// is an "unused function" for coverage purposes.
fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
let MonoItemPartitions { all_mono_items, codegen_units, .. } =
tcx.collect_and_partition_mono_items(());
// Obtain a MIR body for each function participating in codegen, via an
// arbitrary instance.
let mut def_ids_seen = FxHashSet::default();
let def_and_mir_for_all_mono_fns = codegen_units
.iter()
.flat_map(|cgu| cgu.items().keys())
.filter_map(|item| match item {
mir::mono::MonoItem::Fn(instance) => Some(instance),
mir::mono::MonoItem::Static(_) | mir::mono::MonoItem::GlobalAsm(_) => None,
})
// We only need one arbitrary instance per definition.
.filter(move |instance| def_ids_seen.insert(instance.def_id()))
.map(|instance| {
// We don't care about the instance, just its underlying MIR.
let body = tcx.instance_mir(instance.def);
(instance.def_id(), body)
});
// Functions whose coverage statements were found inlined into other functions.
let mut used_via_inlining = FxHashSet::default();
// Functions that were instrumented, but had all of their coverage statements
// removed by later MIR transforms (e.g. UnreachablePropagation).
let mut missing_own_coverage = FxHashSet::default();
for (def_id, body) in def_and_mir_for_all_mono_fns {
let mut saw_own_coverage = false;
// Inspect every coverage statement in the function's MIR.
for stmt in body
.basic_blocks
.iter()
.flat_map(|block| &block.statements)
.filter(|stmt| matches!(stmt.kind, mir::StatementKind::Coverage(_)))
{
if let Some(inlined) = stmt.source_info.scope.inlined_instance(&body.source_scopes) {
// This coverage statement was inlined from another function.
used_via_inlining.insert(inlined.def_id());
} else {
// Non-inlined coverage statements belong to the enclosing function.
saw_own_coverage = true;
}
}
if !saw_own_coverage && body.function_coverage_info.is_some() {
missing_own_coverage.insert(def_id);
}
}
UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
}
fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> {
let def_id = local_def_id.to_def_id();
// Make a dummy instance that fills in all generics with placeholders.
ty::Instance::new(
def_id,
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
if let ty::GenericParamDefKind::Lifetime = param.kind {
tcx.lifetimes.re_erased.into()
} else {
tcx.mk_param_from_def(param)
}
}),
)
}

View file

@ -3,7 +3,7 @@ use std::iter;
use rustc_index::IndexVec;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::{Local, UnwindTerminateReason, traversal};
use rustc_middle::mir::{Body, Local, UnwindTerminateReason, traversal};
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_middle::{bug, mir, span_bug};
@ -170,19 +170,29 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
) {
assert!(!instance.args.has_infer());
let tcx = cx.tcx();
let llfn = cx.get_fn(instance);
let mir = cx.tcx().instance_mir(instance.def);
let mut mir = tcx.instance_mir(instance.def);
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
debug!("fn_abi: {:?}", fn_abi);
if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, &mir, instance);
return;
}
let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir);
if tcx.features().ergonomic_clones() {
let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions(
tcx,
ty::TypingEnv::fully_monomorphized(),
ty::EarlyBinder::bind(mir.clone()),
);
mir = tcx.arena.alloc(optimize_use_clone::<Bx>(cx, monomorphized_mir));
}
let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, &mir);
let start_llbb = Bx::append_block(cx, llfn, "start");
let mut start_bx = Bx::build(cx, start_llbb);
@ -194,7 +204,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
let cleanup_kinds =
base::wants_new_eh_instructions(cx.tcx().sess).then(|| analyze::cleanup_kinds(mir));
base::wants_new_eh_instructions(tcx.sess).then(|| analyze::cleanup_kinds(&mir));
let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> =
mir.basic_blocks
@ -217,7 +227,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cleanup_kinds,
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
cold_blocks: find_cold_blocks(cx.tcx(), mir),
cold_blocks: find_cold_blocks(tcx, mir),
locals: locals::Locals::empty(),
debug_context,
per_local_var_debug_info: None,
@ -233,7 +243,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
fx.compute_per_local_var_debug_info(&mut start_bx).unzip();
fx.per_local_var_debug_info = per_local_var_debug_info;
let traversal_order = traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance);
let traversal_order = traversal::mono_reachable_reverse_postorder(mir, tcx, instance);
let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order);
// Allocate variable and temp allocas
@ -310,6 +320,61 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
}
// FIXME: Move this function to mir::transform when post-mono MIR passes land.
fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cx: &'a Bx::CodegenCx,
mut mir: Body<'tcx>,
) -> Body<'tcx> {
let tcx = cx.tcx();
if tcx.features().ergonomic_clones() {
for bb in mir.basic_blocks.as_mut() {
let mir::TerminatorKind::Call {
args,
destination,
target,
call_source: mir::CallSource::Use,
..
} = &bb.terminator().kind
else {
continue;
};
// CallSource::Use calls always use 1 argument.
assert_eq!(args.len(), 1);
let arg = &args[0];
// These types are easily available from locals, so check that before
// doing DefId lookups to figure out what we're actually calling.
let arg_ty = arg.node.ty(&mir.local_decls, tcx);
let ty::Ref(_region, inner_ty, mir::Mutability::Not) = *arg_ty.kind() else { continue };
if !tcx.type_is_copy_modulo_regions(cx.typing_env(), inner_ty) {
continue;
}
let Some(arg_place) = arg.node.place() else { continue };
let destination_block = target.unwrap();
bb.statements.push(mir::Statement {
source_info: bb.terminator().source_info,
kind: mir::StatementKind::Assign(Box::new((
*destination,
mir::Rvalue::Use(mir::Operand::Copy(
arg_place.project_deeper(&[mir::ProjectionElem::Deref], tcx),
)),
))),
});
bb.terminator_mut().kind = mir::TerminatorKind::Goto { target: destination_block };
}
}
mir
}
/// Produces, for each argument, a `Value` pointing at the
/// argument's value. As arguments are places, these are always
/// indirect.

View file

@ -9,6 +9,7 @@ use rustc_middle::mir::{self, ConstValue};
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::{bug, span_bug};
use rustc_session::config::OptLevel;
use tracing::{debug, instrument};
use super::place::{PlaceRef, PlaceValue};
@ -496,6 +497,18 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
_ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)),
};
// Layout ensures that we only get here for cases where the discriminant
// value and the variant index match, since that's all `Niche` can encode.
// But for emphasis and debugging, let's double-check one anyway.
debug_assert_eq!(
self.layout
.ty
.discriminant_for_variant(bx.tcx(), untagged_variant)
.unwrap()
.val,
u128::from(untagged_variant.as_u32()),
);
let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
// We have a subrange `niche_start..=niche_end` inside `range`.
@ -537,6 +550,21 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
relative_discr,
bx.cx().const_uint(tag_llty, relative_max as u64),
);
// Thanks to parameter attributes and load metadata, LLVM already knows
// the general valid range of the tag. It's possible, though, for there
// to be an impossible value *in the middle*, which those ranges don't
// communicate, so it's worth an `assume` to let the optimizer know.
if niche_variants.contains(&untagged_variant)
&& bx.cx().sess().opts.optimize != OptLevel::No
{
let impossible =
u64::from(untagged_variant.as_u32() - niche_variants.start().as_u32());
let impossible = bx.cx().const_uint(tag_llty, impossible);
let ne = bx.icmp(IntPredicate::IntNE, relative_discr, impossible);
bx.assume(ne);
}
(is_niche, cast_tag, niche_variants.start().as_u32() as u128)
};
@ -553,7 +581,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
);
// In principle we could insert assumes on the possible range of `discr`, but
// currently in LLVM this seems to be a pessimization.
// currently in LLVM this isn't worth it because the original `tag` will
// have either a `range` parameter attribute or `!range` metadata,
// or come from a `transmute` that already `assume`d it.
discr
}

View file

@ -315,7 +315,7 @@ mod helper {
use super::*;
pub(super) type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;
impl<O: ForestObligation> ObligationForest<O> {
#[cfg_attr(not(bootstrap), define_opaque(ObligationTreeIdGenerator))]
#[define_opaque(ObligationTreeIdGenerator)]
pub fn new() -> ObligationForest<O> {
ObligationForest {
nodes: vec![],

View file

@ -6,8 +6,9 @@ Erroneous code example:
#![feature(intrinsics)]
#![allow(internal_features)]
extern "rust-intrinsic" {
pub static atomic_singlethreadfence_seqcst: fn();
extern "C" {
#[rustc_intrinsic]
pub static atomic_singlethreadfence_seqcst: unsafe fn();
// error: intrinsic must be a function
}
@ -22,9 +23,8 @@ error, just declare a function. Example:
#![feature(intrinsics)]
#![allow(internal_features)]
extern "rust-intrinsic" {
pub fn atomic_singlethreadfence_seqcst(); // ok!
}
#[rustc_intrinsic]
pub unsafe fn atomic_singlethreadfence_seqcst(); // ok!
fn main() { unsafe { atomic_singlethreadfence_seqcst(); } }
```

View file

@ -208,7 +208,7 @@ pub type LazyFallbackBundle = Arc<LazyLock<FluentBundle, impl FnOnce() -> Fluent
/// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
#[instrument(level = "trace", skip(resources))]
#[cfg_attr(not(bootstrap), define_opaque(LazyFallbackBundle))]
#[define_opaque(LazyFallbackBundle)]
pub fn fallback_fluent_bundle(
resources: Vec<&'static str>,
with_directionality_markers: bool,

View file

@ -647,9 +647,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
#[rustc_lint_diagnostics]
pub fn note_expected_found(
&mut self,
expected_label: &dyn fmt::Display,
expected_label: &str,
expected: DiagStyledString,
found_label: &dyn fmt::Display,
found_label: &str,
found: DiagStyledString,
) -> &mut Self {
self.note_expected_found_extra(
@ -665,9 +665,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
#[rustc_lint_diagnostics]
pub fn note_expected_found_extra(
&mut self,
expected_label: &dyn fmt::Display,
expected_label: &str,
expected: DiagStyledString,
found_label: &dyn fmt::Display,
found_label: &str,
found: DiagStyledString,
expected_extra: DiagStyledString,
found_extra: DiagStyledString,

View file

@ -589,7 +589,8 @@ struct DiagCtxtInner {
/// add more information). All stashed diagnostics must be emitted with
/// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
/// otherwise an assertion failure will occur.
stashed_diagnostics: FxIndexMap<(Span, StashKey), (DiagInner, Option<ErrorGuaranteed>)>,
stashed_diagnostics:
FxIndexMap<StashKey, FxIndexMap<Span, (DiagInner, Option<ErrorGuaranteed>)>>,
future_breakage_diagnostics: Vec<DiagInner>,
@ -912,8 +913,12 @@ impl<'a> DiagCtxtHandle<'a> {
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
// if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
// See the PR for a discussion.
let key = (span.with_parent(None), key);
self.inner.borrow_mut().stashed_diagnostics.insert(key, (diag, guar));
self.inner
.borrow_mut()
.stashed_diagnostics
.entry(key)
.or_default()
.insert(span.with_parent(None), (diag, guar));
guar
}
@ -922,9 +927,10 @@ impl<'a> DiagCtxtHandle<'a> {
/// and [`StashKey`] as the key. Panics if the found diagnostic is an
/// error.
pub fn steal_non_err(self, span: Span, key: StashKey) -> Option<Diag<'a, ()>> {
let key = (span.with_parent(None), key);
// FIXME(#120456) - is `swap_remove` correct?
let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key)?;
let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
)?;
assert!(!diag.is_error());
assert!(guar.is_none());
Some(Diag::new_diagnostic(self, diag))
@ -943,9 +949,10 @@ impl<'a> DiagCtxtHandle<'a> {
where
F: FnMut(&mut Diag<'_>),
{
let key = (span.with_parent(None), key);
// FIXME(#120456) - is `swap_remove` correct?
let err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key);
let err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
);
err.map(|(err, guar)| {
// The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`.
assert_eq!(err.level, Error);
@ -966,9 +973,10 @@ impl<'a> DiagCtxtHandle<'a> {
key: StashKey,
new_err: Diag<'_>,
) -> ErrorGuaranteed {
let key = (span.with_parent(None), key);
// FIXME(#120456) - is `swap_remove` correct?
let old_err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key);
let old_err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
);
match old_err {
Some((old_err, guar)) => {
assert_eq!(old_err.level, Error);
@ -983,7 +991,14 @@ impl<'a> DiagCtxtHandle<'a> {
}
pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
self.inner.borrow().stashed_diagnostics.get(&(span.with_parent(None), key)).is_some()
let inner = self.inner.borrow();
if let Some(stashed_diagnostics) = inner.stashed_diagnostics.get(&key)
&& !stashed_diagnostics.is_empty()
{
stashed_diagnostics.contains_key(&span.with_parent(None))
} else {
false
}
}
/// Emit all stashed diagnostics.
@ -997,7 +1012,11 @@ impl<'a> DiagCtxtHandle<'a> {
let inner = self.inner.borrow();
inner.err_guars.len()
+ inner.lint_err_guars.len()
+ inner.stashed_diagnostics.values().filter(|(_diag, guar)| guar.is_some()).count()
+ inner
.stashed_diagnostics
.values()
.map(|a| a.values().filter(|(_, guar)| guar.is_some()).count())
.sum::<usize>()
}
/// This excludes lint errors and delayed bugs. Unless absolutely
@ -1486,16 +1505,18 @@ impl DiagCtxtInner {
fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
let mut guar = None;
let has_errors = !self.err_guars.is_empty();
for (_, (diag, _guar)) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
if !diag.is_error() {
// Unless they're forced, don't flush stashed warnings when
// there are errors, to avoid causing warning overload. The
// stash would've been stolen already if it were important.
if !diag.is_force_warn() && has_errors {
continue;
for (_, stashed_diagnostics) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
for (_, (diag, _guar)) in stashed_diagnostics {
if !diag.is_error() {
// Unless they're forced, don't flush stashed warnings when
// there are errors, to avoid causing warning overload. The
// stash would've been stolen already if it were important.
if !diag.is_force_warn() && has_errors {
continue;
}
}
guar = guar.or(self.emit_diagnostic(diag, None));
}
guar = guar.or(self.emit_diagnostic(diag, None));
}
guar
}
@ -1688,6 +1709,7 @@ impl DiagCtxtInner {
if let Some((_diag, guar)) = self
.stashed_diagnostics
.values()
.flat_map(|stashed_diagnostics| stashed_diagnostics.values())
.find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
{
*guar
@ -1700,13 +1722,9 @@ impl DiagCtxtInner {
fn has_errors(&self) -> Option<ErrorGuaranteed> {
self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else(
|| {
if let Some((_diag, guar)) =
self.stashed_diagnostics.values().find(|(_diag, guar)| guar.is_some())
{
*guar
} else {
None
}
self.stashed_diagnostics.values().find_map(|stashed_diagnostics| {
stashed_diagnostics.values().find_map(|(_, guar)| *guar)
})
},
)
}

View file

@ -230,6 +230,7 @@ impl<'a> ExtCtxt<'a> {
self.pat_ident(sp, ident)
};
let local = P(ast::Local {
super_: None,
pat,
ty,
id: ast::DUMMY_NODE_ID,
@ -245,6 +246,7 @@ impl<'a> ExtCtxt<'a> {
/// Generates `let _: Type;`, which is usually used for type assertions.
pub fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt {
let local = P(ast::Local {
super_: None,
pat: self.pat_wild(span),
ty: Some(ty),
id: ast::DUMMY_NODE_ID,

View file

@ -63,7 +63,7 @@ declare_features! (
/// Allows using `const` operands in inline assembly.
(accepted, asm_const, "1.82.0", Some(93332)),
/// Allows using `label` operands in inline assembly.
(accepted, asm_goto, "CURRENT_RUSTC_VERSION", Some(119364)),
(accepted, asm_goto, "1.87.0", Some(119364)),
/// Allows using `sym` operands in inline assembly.
(accepted, asm_sym, "1.66.0", Some(93333)),
/// Allows the definition of associated constants in `trait` or `impl` blocks.
@ -332,7 +332,7 @@ declare_features! (
/// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args.
(accepted, precise_capturing, "1.82.0", Some(123432)),
/// Allows `use<..>` precise capturign on impl Trait in traits.
(accepted, precise_capturing_in_traits, "CURRENT_RUSTC_VERSION", Some(130044)),
(accepted, precise_capturing_in_traits, "1.87.0", Some(130044)),
/// Allows procedural macros in `proc-macro` crates.
(accepted, proc_macro, "1.29.0", Some(38356)),
/// Allows multi-segment paths in attributes and derives.

View file

@ -247,7 +247,7 @@ declare_features! (
/// Allows unnamed fields of struct and union type
(removed, unnamed_fields, "1.83.0", Some(49804), Some("feature needs redesign")),
(removed, unsafe_no_drop_flag, "1.0.0", None, None),
(removed, unsized_tuple_coercion, "CURRENT_RUSTC_VERSION", Some(42877),
(removed, unsized_tuple_coercion, "1.87.0", Some(42877),
Some("The feature restricts possible layouts for tuples, and this restriction is not worth it.")),
/// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue.
(removed, untagged_unions, "1.13.0", Some(55149),

View file

@ -211,7 +211,7 @@ declare_features! (
(internal, custom_mir, "1.65.0", None),
/// Outputs useful `assert!` messages
(unstable, generic_assert, "1.63.0", None),
/// Allows using the `rust-intrinsic`'s "ABI".
/// Allows using the #[rustc_intrinsic] attribute.
(internal, intrinsics, "1.0.0", None),
/// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
(internal, lang_items, "1.0.0", None),
@ -474,7 +474,7 @@ declare_features! (
/// Allows `dyn* Trait` objects.
(incomplete, dyn_star, "1.65.0", Some(102425)),
/// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }`
(incomplete, ergonomic_clones, "CURRENT_RUSTC_VERSION", Some(132290)),
(incomplete, ergonomic_clones, "1.87.0", Some(132290)),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
(unstable, exhaustive_patterns, "1.13.0", Some(51085)),
/// Allows explicit tail calls via `become` expression.
@ -511,7 +511,7 @@ declare_features! (
/// Allows generic parameters and where-clauses on free & associated const items.
(incomplete, generic_const_items, "1.73.0", Some(113521)),
/// Allows the type of const generics to depend on generic parameters
(incomplete, generic_const_parameter_types, "CURRENT_RUSTC_VERSION", Some(137626)),
(incomplete, generic_const_parameter_types, "1.87.0", Some(137626)),
/// Allows any generic constants being used as pattern type range ends
(incomplete, generic_pattern_types, "1.86.0", Some(136574)),
/// Allows registering static items globally, possibly across crates, to iterate over at runtime.
@ -602,7 +602,7 @@ declare_features! (
/// Allows macro attributes on expressions, statements and non-inline modules.
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
/// Allows the use of raw-dylibs on ELF platforms
(incomplete, raw_dylib_elf, "CURRENT_RUSTC_VERSION", Some(135694)),
(incomplete, raw_dylib_elf, "1.87.0", Some(135694)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
(incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
@ -630,7 +630,7 @@ declare_features! (
/// Allows string patterns to dereference values to match them.
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
/// Allows `super let` statements.
(incomplete, super_let, "CURRENT_RUSTC_VERSION", Some(139076)),
(unstable, super_let, "CURRENT_RUSTC_VERSION", Some(139076)),
/// Allows subtrait items to shadow supertrait items.
(unstable, supertrait_item_shadowing, "1.86.0", Some(89151)),
/// Allows using `#[thread_local]` on `static` items.
@ -664,14 +664,14 @@ declare_features! (
/// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
(unstable, used_with_arg, "1.60.0", Some(93798)),
/// Allows use of attributes in `where` clauses.
(unstable, where_clause_attrs, "CURRENT_RUSTC_VERSION", Some(115590)),
(unstable, where_clause_attrs, "1.87.0", Some(115590)),
/// Allows use of x86 `AMX` target-feature attributes and intrinsics
(unstable, x86_amx_intrinsics, "1.81.0", Some(126622)),
/// Allows use of the `xop` target-feature
(unstable, xop_target_feature, "1.81.0", Some(127208)),
/// Allows `do yeet` expressions
(unstable, yeet_expr, "1.62.0", Some(96373)),
(unstable, yield_expr, "CURRENT_RUSTC_VERSION", Some(43122)),
(unstable, yield_expr, "1.87.0", Some(43122)),
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!

View file

@ -1555,6 +1555,7 @@ impl<'hir> Pat<'hir> {
use PatKind::*;
match self.kind {
Missing => unreachable!(),
Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true,
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it),
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
@ -1582,7 +1583,7 @@ impl<'hir> Pat<'hir> {
use PatKind::*;
match self.kind {
Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {}
Missing | Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {}
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
@ -1720,6 +1721,9 @@ pub enum TyPatKind<'hir> {
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum PatKind<'hir> {
/// A missing pattern, e.g. for an anonymous param in a bare fn like `fn f(u32)`.
Missing,
/// Represents a wildcard pattern (i.e., `_`).
Wild,
@ -1817,6 +1821,8 @@ pub enum StmtKind<'hir> {
/// Represents a `let` statement (i.e., `let <pat>:<ty> = <init>;`).
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct LetStmt<'hir> {
/// Span of `super` in `super let`.
pub super_: Option<Span>,
pub pat: &'hir Pat<'hir>,
/// Type annotation, if any (otherwise the type will be inferred).
pub ty: Option<&'hir Ty<'hir>>,
@ -4850,7 +4856,7 @@ mod size_asserts {
static_assert_size!(ImplItemKind<'_>, 40);
static_assert_size!(Item<'_>, 88);
static_assert_size!(ItemKind<'_>, 64);
static_assert_size!(LetStmt<'_>, 64);
static_assert_size!(LetStmt<'_>, 72);
static_assert_size!(Param<'_>, 32);
static_assert_size!(Pat<'_>, 72);
static_assert_size!(Path<'_>, 40);

View file

@ -744,7 +744,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V:
visit_opt!(visitor, visit_pat_expr, lower_bound);
visit_opt!(visitor, visit_pat_expr, upper_bound);
}
PatKind::Never | PatKind::Wild | PatKind::Err(_) => (),
PatKind::Missing | PatKind::Never | PatKind::Wild | PatKind::Err(_) => (),
PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => {
walk_list!(visitor, visit_pat, prepatterns);
visit_opt!(visitor, visit_pat, slice_pattern);

View file

@ -486,6 +486,9 @@ hir_analysis_self_in_impl_self =
`Self` is not valid in the self type of an impl block
.note = replace `Self` with a different type
hir_analysis_self_in_type_alias = `Self` is not allowed in type aliases
.label = `Self` is only available in impls, traits, and concrete type definitions
hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>`
.label = `Self` type parameter is implicitly captured by this `impl Trait`
.note = currently, all type parameters are required to be mentioned in the precise captures list

View file

@ -397,8 +397,11 @@ fn best_definition_site_of_opaque<'tcx>(
return ControlFlow::Continue(());
}
if let Some(hidden_ty) =
self.tcx.mir_borrowck(item_def_id).concrete_opaque_types.get(&self.opaque_def_id)
if let Some(hidden_ty) = self
.tcx
.mir_borrowck(item_def_id)
.ok()
.and_then(|opaque_types| opaque_types.0.get(&self.opaque_def_id))
{
ControlFlow::Break((hidden_ty.span, item_def_id))
} else {
@ -413,9 +416,6 @@ fn best_definition_site_of_opaque<'tcx>(
self.tcx
}
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result {
if let hir::ExprKind::Closure(closure) = ex.kind {
self.check(closure.def_id)?;
}
intravisit::walk_expr(self, ex)
}
fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) -> Self::Result {
@ -741,10 +741,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
for &assoc_item in assoc_items.in_definition_order() {
match assoc_item.kind {
ty::AssocKind::Fn => {
let abi = tcx.fn_sig(assoc_item.def_id).skip_binder().abi();
forbid_intrinsic_abi(tcx, assoc_item.ident(tcx).span, abi);
}
ty::AssocKind::Type if assoc_item.defaultness(tcx).has_value() => {
let trait_args = GenericArgs::identity_for_item(tcx, def_id);
let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds(
@ -788,65 +784,59 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
};
check_abi(tcx, it.span, abi);
match abi {
ExternAbi::RustIntrinsic => {
for item in items {
intrinsic::check_intrinsic_type(
tcx,
item.id.owner_id.def_id,
item.span,
item.ident.name,
abi,
);
}
for item in items {
let def_id = item.id.owner_id.def_id;
if tcx.has_attr(def_id, sym::rustc_intrinsic) {
intrinsic::check_intrinsic_type(
tcx,
item.id.owner_id.def_id,
item.span,
item.ident.name,
abi,
);
}
_ => {
for item in items {
let def_id = item.id.owner_id.def_id;
let generics = tcx.generics_of(def_id);
let own_counts = generics.own_counts();
if generics.own_params.len() - own_counts.lifetimes != 0 {
let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts)
{
(_, 0) => ("type", "types", Some("u32")),
// We don't specify an example value, because we can't generate
// a valid value for any type.
(0, _) => ("const", "consts", None),
_ => ("type or const", "types or consts", None),
};
struct_span_code_err!(
tcx.dcx(),
item.span,
E0044,
"foreign items may not have {kinds} parameters",
)
.with_span_label(item.span, format!("can't have {kinds} parameters"))
.with_help(
// FIXME: once we start storing spans for type arguments, turn this
// into a suggestion.
format!(
"replace the {} parameters with concrete {}{}",
kinds,
kinds_pl,
egs.map(|egs| format!(" like `{egs}`")).unwrap_or_default(),
),
)
.emit();
}
let generics = tcx.generics_of(def_id);
let own_counts = generics.own_counts();
if generics.own_params.len() - own_counts.lifetimes != 0 {
let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) {
(_, 0) => ("type", "types", Some("u32")),
// We don't specify an example value, because we can't generate
// a valid value for any type.
(0, _) => ("const", "consts", None),
_ => ("type or const", "types or consts", None),
};
struct_span_code_err!(
tcx.dcx(),
item.span,
E0044,
"foreign items may not have {kinds} parameters",
)
.with_span_label(item.span, format!("can't have {kinds} parameters"))
.with_help(
// FIXME: once we start storing spans for type arguments, turn this
// into a suggestion.
format!(
"replace the {} parameters with concrete {}{}",
kinds,
kinds_pl,
egs.map(|egs| format!(" like `{egs}`")).unwrap_or_default(),
),
)
.emit();
}
let item = tcx.hir_foreign_item(item.id);
match &item.kind {
hir::ForeignItemKind::Fn(sig, _, _) => {
require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span);
}
hir::ForeignItemKind::Static(..) => {
check_static_inhabited(tcx, def_id);
check_static_linkage(tcx, def_id);
}
_ => {}
}
let item = tcx.hir_foreign_item(item.id);
match &item.kind {
hir::ForeignItemKind::Fn(sig, _, _) => {
require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span);
}
hir::ForeignItemKind::Static(..) => {
check_static_inhabited(tcx, def_id);
check_static_linkage(tcx, def_id);
}
_ => {}
}
}
}
@ -947,31 +937,7 @@ fn check_impl_items_against_trait<'tcx>(
let trait_def = tcx.trait_def(trait_ref.def_id);
let infcx = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis());
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let cause = ObligationCause::misc(tcx.def_span(impl_id), impl_id);
let param_env = tcx.param_env(impl_id);
let self_is_guaranteed_unsized = match tcx
.struct_tail_raw(
trait_ref.self_ty(),
|ty| {
ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| {
Ty::new_error_with_message(
tcx,
tcx.def_span(impl_id),
"struct tail should be computable",
)
})
},
|| (),
)
.kind()
{
ty::Dynamic(_, _, ty::DynKind::Dyn) | ty::Slice(_) | ty::Str => true,
_ => false,
};
let self_is_guaranteed_unsize_self = tcx.impl_self_is_guaranteed_unsized(impl_id);
for &impl_item in impl_item_refs {
let ty_impl_item = tcx.associated_item(impl_item);
@ -1002,7 +968,7 @@ fn check_impl_items_against_trait<'tcx>(
}
}
if self_is_guaranteed_unsized && tcx.generics_require_sized_self(ty_trait_item.def_id) {
if self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(ty_trait_item.def_id) {
tcx.emit_node_span_lint(
rustc_lint_defs::builtin::DEAD_CODE,
tcx.local_def_id_to_hir_id(ty_impl_item.def_id.expect_local()),
@ -1037,7 +1003,7 @@ fn check_impl_items_against_trait<'tcx>(
if !is_implemented
&& tcx.defaultness(impl_id).is_final()
// unsized types don't need to implement methods that have `Self: Sized` bounds.
&& !(self_is_guaranteed_unsized && tcx.generics_require_sized_self(trait_item_id))
&& !(self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(trait_item_id))
{
missing_items.push(tcx.associated_item(trait_item_id));
}

View file

@ -442,7 +442,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateParam<'tcx> {
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
if let ty::ReLateParam(fr) = *r {
if let ty::ReLateParam(fr) = r.kind() {
ty::Region::new_late_param(
self.tcx,
fr.scope,

View file

@ -1,4 +1,4 @@
//! Type-checking for the rust-intrinsic intrinsics that the compiler exposes.
//! Type-checking for the `#[rustc_intrinsic]` intrinsics that the compiler exposes.
use rustc_abi::ExternAbi;
use rustc_errors::codes::*;

View file

@ -137,15 +137,6 @@ fn get_owner_return_paths(
})
}
/// Forbid defining intrinsics in Rust code,
/// as they must always be defined by the compiler.
// FIXME: Move this to a more appropriate place.
pub fn forbid_intrinsic_abi(tcx: TyCtxt<'_>, sp: Span, abi: ExternAbi) {
if let ExternAbi::RustIntrinsic = abi {
tcx.dcx().span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block");
}
}
pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
// Only restricted on wasm target for now
if !tcx.sess.target.is_like_wasm {

View file

@ -8,6 +8,7 @@
use std::mem;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
@ -44,6 +45,8 @@ struct ScopeResolutionVisitor<'tcx> {
scope_tree: ScopeTree,
cx: Context,
extended_super_lets: FxHashMap<hir::ItemLocalId, Option<Scope>>,
}
/// Records the lifetime of a local variable as `cx.var_parent`
@ -214,18 +217,29 @@ fn resolve_stmt<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, stmt: &'tcx hi
let stmt_id = stmt.hir_id.local_id;
debug!("resolve_stmt(stmt.id={:?})", stmt_id);
// Every statement will clean up the temporaries created during
// execution of that statement. Therefore each statement has an
// associated destruction scope that represents the scope of the
// statement plus its destructors, and thus the scope for which
// regions referenced by the destructors need to survive.
if let hir::StmtKind::Let(LetStmt { super_: Some(_), .. }) = stmt.kind {
// `super let` statement does not start a new scope, such that
//
// { super let x = identity(&temp()); &x }.method();
//
// behaves exactly as
//
// (&identity(&temp()).method();
intravisit::walk_stmt(visitor, stmt);
} else {
// Every statement will clean up the temporaries created during
// execution of that statement. Therefore each statement has an
// associated destruction scope that represents the scope of the
// statement plus its destructors, and thus the scope for which
// regions referenced by the destructors need to survive.
let prev_parent = visitor.cx.parent;
visitor.enter_node_scope_with_dtor(stmt_id, true);
let prev_parent = visitor.cx.parent;
visitor.enter_node_scope_with_dtor(stmt_id, true);
intravisit::walk_stmt(visitor, stmt);
intravisit::walk_stmt(visitor, stmt);
visitor.cx.parent = prev_parent;
visitor.cx.parent = prev_parent;
}
}
fn resolve_expr<'tcx>(
@ -446,14 +460,11 @@ fn resolve_expr<'tcx>(
// Mark this expr's scope and all parent scopes as containing `yield`.
let mut scope = Scope { local_id: expr.hir_id.local_id, data: ScopeData::Node };
loop {
let span = match expr.kind {
hir::ExprKind::Yield(expr, hir::YieldSource::Await { .. }) => {
expr.span.shrink_to_hi().to(expr.span)
}
_ => expr.span,
let data = YieldData {
span: expr.span,
expr_and_pat_count: visitor.expr_and_pat_count,
source: *source,
};
let data =
YieldData { span, expr_and_pat_count: visitor.expr_and_pat_count, source: *source };
match visitor.scope_tree.yield_in_scope.get_mut(&scope) {
Some(yields) => yields.push(data),
None => {
@ -481,14 +492,19 @@ fn resolve_expr<'tcx>(
visitor.cx = prev_cx;
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum LetKind {
Regular,
Super,
}
fn resolve_local<'tcx>(
visitor: &mut ScopeResolutionVisitor<'tcx>,
pat: Option<&'tcx hir::Pat<'tcx>>,
init: Option<&'tcx hir::Expr<'tcx>>,
let_kind: LetKind,
) {
debug!("resolve_local(pat={:?}, init={:?})", pat, init);
let blk_scope = visitor.cx.var_parent;
debug!("resolve_local(pat={:?}, init={:?}, let_kind={:?})", pat, init, let_kind);
// As an exception to the normal rules governing temporary
// lifetimes, initializers in a let have a temporary lifetime
@ -546,14 +562,50 @@ fn resolve_local<'tcx>(
// A, but the inner rvalues `a()` and `b()` have an extended lifetime
// due to rule C.
if let_kind == LetKind::Super {
if let Some(scope) = visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) {
// This expression was lifetime-extended by a parent let binding. E.g.
//
// let a = {
// super let b = temp();
// &b
// };
//
// (Which needs to behave exactly as: let a = &temp();)
//
// Processing of `let a` will have already decided to extend the lifetime of this
// `super let` to its own var_scope. We use that scope.
visitor.cx.var_parent = scope;
} else {
// This `super let` is not subject to lifetime extension from a parent let binding. E.g.
//
// identity({ super let x = temp(); &x }).method();
//
// (Which needs to behave exactly as: identity(&temp()).method();)
//
// Iterate up to the enclosing destruction scope to find the same scope that will also
// be used for the result of the block itself.
while let Some(s) = visitor.cx.var_parent {
let parent = visitor.scope_tree.parent_map.get(&s).cloned();
if let Some(Scope { data: ScopeData::Destruction, .. }) = parent {
break;
}
visitor.cx.var_parent = parent;
}
}
}
if let Some(expr) = init {
record_rvalue_scope_if_borrow_expr(visitor, expr, blk_scope);
record_rvalue_scope_if_borrow_expr(visitor, expr, visitor.cx.var_parent);
if let Some(pat) = pat {
if is_binding_pat(pat) {
visitor.scope_tree.record_rvalue_candidate(
expr.hir_id,
RvalueCandidate { target: expr.hir_id.local_id, lifetime: blk_scope },
RvalueCandidate {
target: expr.hir_id.local_id,
lifetime: visitor.cx.var_parent,
},
);
}
}
@ -565,6 +617,7 @@ fn resolve_local<'tcx>(
if let Some(expr) = init {
visitor.visit_expr(expr);
}
if let Some(pat) = pat {
visitor.visit_pat(pat);
}
@ -626,6 +679,7 @@ fn resolve_local<'tcx>(
PatKind::Ref(_, _)
| PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..)
| PatKind::Missing
| PatKind::Wild
| PatKind::Never
| PatKind::Expr(_)
@ -642,6 +696,7 @@ fn resolve_local<'tcx>(
/// | [ ..., E&, ... ]
/// | ( ..., E&, ... )
/// | {...; E&}
/// | { super let ... = E&; ... }
/// | if _ { ...; E& } else { ...; E& }
/// | match _ { ..., _ => E&, ... }
/// | box E&
@ -678,6 +733,13 @@ fn resolve_local<'tcx>(
if let Some(subexpr) = block.expr {
record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
}
for stmt in block.stmts {
if let hir::StmtKind::Let(local) = stmt.kind
&& let Some(_) = local.super_
{
visitor.extended_super_lets.insert(local.pat.hir_id.local_id, blk_id);
}
}
}
hir::ExprKind::If(_, then_block, else_block) => {
record_rvalue_scope_if_borrow_expr(visitor, then_block, blk_id);
@ -803,7 +865,7 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
local_id: body.value.hir_id.local_id,
data: ScopeData::Destruction,
});
resolve_local(this, None, Some(body.value));
resolve_local(this, None, Some(body.value), LetKind::Regular);
}
})
}
@ -821,7 +883,11 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> {
resolve_expr(self, ex, false);
}
fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) {
resolve_local(self, Some(l.pat), l.init)
let let_kind = match l.super_ {
Some(_) => LetKind::Super,
None => LetKind::Regular,
};
resolve_local(self, Some(l.pat), l.init, let_kind);
}
fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
let body = self.tcx.hir_body(c.body);
@ -850,6 +916,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
cx: Context { parent: None, var_parent: None },
pessimistic_yield: false,
fixup_scopes: vec![],
extended_super_lets: Default::default(),
};
visitor.scope_tree.root_body = Some(body.value.hir_id);

View file

@ -631,7 +631,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
// Ignore `'static` lifetimes for the purpose of this lint: it's
// because we know it outlives everything and so doesn't give meaningful
// clues. Also ignore `ReError`, to avoid knock-down errors.
if let ty::ReStatic | ty::ReError(_) = **region_a {
if let ty::ReStatic | ty::ReError(_) = region_a.kind() {
continue;
}
// For each region argument (e.g., `'a` in our example), check for a
@ -672,7 +672,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
// Again, skip `'static` because it outlives everything. Also, we trivially
// know that a region outlives itself. Also ignore `ReError`, to avoid
// knock-down errors.
if matches!(**region_b, ty::ReStatic | ty::ReError(_)) || region_a == region_b {
if matches!(region_b.kind(), ty::ReStatic | ty::ReError(_)) || region_a == region_b {
continue;
}
if region_known_to_outlive(tcx, item_def_id, param_env, wf_tys, *region_a, *region_b) {

View file

@ -656,7 +656,7 @@ fn infringing_fields_error<'tcx>(
.entry((ty.clone(), predicate.clone()))
.or_default()
.push(origin.span());
if let ty::RegionKind::ReEarlyParam(ebr) = *b
if let ty::RegionKind::ReEarlyParam(ebr) = b.kind()
&& ebr.has_name()
{
bounds.push((b.to_string(), a.to_string(), None));

View file

@ -14,6 +14,7 @@
//! At present, however, we do run collection across all items in the
//! crate as a kind of pass. This should eventually be factored away.
use std::assert_matches::assert_matches;
use std::cell::Cell;
use std::iter;
use std::ops::Bound;
@ -42,7 +43,6 @@ use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::ObligationCtxt;
use tracing::{debug, instrument};
use crate::check::intrinsic::intrinsic_operation_unsafety;
use crate::errors;
use crate::hir_ty_lowering::errors::assoc_kind_str;
use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};
@ -1345,7 +1345,8 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety())
}
Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => {
Ctor(data) => {
assert_matches!(data.ctor(), Some(_));
let adt_def_id = tcx.hir_get_parent_item(hir_id).def_id.to_def_id();
let ty = tcx.type_of(adt_def_id).instantiate_identity();
let inputs = data.fields().iter().map(|f| tcx.type_of(f.def_id).instantiate_identity());
@ -1417,7 +1418,7 @@ fn recover_infer_ret_ty<'tcx>(
GenericParamKind::Lifetime { .. } => true,
_ => false,
});
let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r {
let fn_sig = fold_regions(tcx, fn_sig, |r, _| match r.kind() {
ty::ReErased => {
if has_region_params {
ty::Region::new_error_with_message(
@ -1704,18 +1705,13 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
abi: ExternAbi,
safety: hir::Safety,
) -> ty::PolyFnSig<'tcx> {
let safety = if abi == ExternAbi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, def_id)
} else {
safety
};
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let fty =
ItemCtxt::new(tcx, def_id).lowerer().lower_fn_ty(hir_id, safety, abi, decl, None, None);
// Feature gate SIMD types in FFI, since I am not sure that the
// ABIs are handled at all correctly. -huonw
if abi != ExternAbi::RustIntrinsic && !tcx.features().simd_ffi() {
if !tcx.features().simd_ffi() {
let check = |hir_ty: &hir::Ty<'_>, ty: Ty<'_>| {
if ty.is_simd() {
let snip = tcx

View file

@ -172,33 +172,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
};
if let Node::TraitItem(item) = node {
let parent = tcx.local_parent(item.hir_id().owner.def_id);
let Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else {
unreachable!();
};
let (trait_generics, trait_bounds) = match parent_trait.kind {
hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => (generics, supertraits),
hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits),
_ => unreachable!(),
};
// Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if
// they are not added as super trait bounds to the trait itself. See comment on
// `requires_default_supertraits` for more details.
if !icx.lowerer().requires_default_supertraits(trait_bounds, trait_generics) {
let mut bounds = Vec::new();
let self_ty_where_predicates = (parent, item.generics.predicates);
icx.lowerer().add_default_traits_with_filter(
&mut bounds,
tcx.types.self_param,
&[],
Some(self_ty_where_predicates),
item.span,
|tr| tr != hir::LangItem::Sized,
);
predicates.extend(bounds);
}
let mut bounds = Vec::new();
icx.lowerer().add_default_trait_item_bounds(item, &mut bounds);
predicates.extend(bounds);
}
let generics = tcx.generics_of(def_id);
@ -383,7 +359,7 @@ fn compute_bidirectional_outlives_predicates<'tcx>(
) {
for param in opaque_own_params {
let orig_lifetime = tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local());
if let ty::ReEarlyParam(..) = *orig_lifetime {
if let ty::ReEarlyParam(..) = orig_lifetime.kind() {
let dup_lifetime = ty::Region::new_early_param(
tcx,
ty::EarlyParamRegion { index: param.index, name: param.name },

View file

@ -183,25 +183,23 @@ impl<'tcx> TaitConstraintLocator<'tcx> {
self.non_defining_use_in_defining_scope(item_def_id);
}
}
DefiningScopeKind::MirBorrowck => {
let borrowck_result = tcx.mir_borrowck(item_def_id);
if let Some(guar) = borrowck_result.tainted_by_errors {
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else if let Some(&hidden_type) =
borrowck_result.concrete_opaque_types.get(&self.def_id)
{
debug!(?hidden_type, "found constraint");
self.insert_found(hidden_type);
} else if let Err(guar) = tcx
.type_of_opaque_hir_typeck(self.def_id)
.instantiate_identity()
.error_reported()
{
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else {
self.non_defining_use_in_defining_scope(item_def_id);
DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(item_def_id) {
Err(guar) => self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)),
Ok(concrete_opaque_types) => {
if let Some(&hidden_type) = concrete_opaque_types.0.get(&self.def_id) {
debug!(?hidden_type, "found constraint");
self.insert_found(hidden_type);
} else if let Err(guar) = tcx
.type_of_opaque_hir_typeck(self.def_id)
.instantiate_identity()
.error_reported()
{
self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
} else {
self.non_defining_use_in_defining_scope(item_def_id);
}
}
}
},
}
}
}
@ -264,20 +262,20 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
Ty::new_diverging_default(tcx)
}
}
DefiningScopeKind::MirBorrowck => {
let borrowck_result = tcx.mir_borrowck(owner_def_id);
if let Some(guar) = borrowck_result.tainted_by_errors {
Ty::new_error(tcx, guar)
} else if let Some(hidden_ty) = borrowck_result.concrete_opaque_types.get(&def_id) {
hidden_ty.ty
} else {
let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity();
if let Err(guar) = hir_ty.error_reported() {
Ty::new_error(tcx, guar)
DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(owner_def_id) {
Ok(concrete_opaque_types) => {
if let Some(hidden_ty) = concrete_opaque_types.0.get(&def_id) {
hidden_ty.ty
} else {
hir_ty
let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity();
if let Err(guar) = hir_ty.error_reported() {
Ty::new_error(tcx, guar)
} else {
hir_ty
}
}
}
}
Err(guar) => Ty::new_error(tcx, guar),
},
}
}

View file

@ -80,7 +80,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
}
fn visit_region(&mut self, r: ty::Region<'tcx>) {
if let ty::ReEarlyParam(data) = *r {
if let ty::ReEarlyParam(data) = r.kind() {
self.parameters.push(Parameter::from(data));
}
}

View file

@ -1707,3 +1707,11 @@ pub(crate) enum SupertraitItemShadowee {
traits: DiagSymbolList,
},
}
#[derive(Diagnostic)]
#[diag(hir_analysis_self_in_type_alias, code = E0411)]
pub(crate) struct SelfInTypeAlias {
#[primary_span]
#[label]
pub span: Span,
}

View file

@ -43,12 +43,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
/// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds
/// or associative items.
/// or associated items.
///
/// To keep backward compatibility with existing code, `experimental_default_bounds` bounds
/// should be added everywhere, including super bounds. However this causes a huge performance
/// costs. For optimization purposes instead of adding default supertraits, bounds
/// are added to the associative items:
/// are added to the associated items:
///
/// ```ignore(illustrative)
/// // Default bounds are generated in the following way:
@ -81,7 +81,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
///
/// Therefore, `experimental_default_bounds` are still being added to supertraits if
/// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header.
pub(crate) fn requires_default_supertraits(
fn requires_default_supertraits(
&self,
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
hir_generics: &'tcx hir::Generics<'tcx>,
@ -120,6 +120,43 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
found
}
/// Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if
/// they are not added as super trait bounds to the trait itself. See
/// `requires_default_supertraits` for more information.
pub(crate) fn add_default_trait_item_bounds(
&self,
trait_item: &hir::TraitItem<'tcx>,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
) {
let tcx = self.tcx();
if !tcx.sess.opts.unstable_opts.experimental_default_bounds {
return;
}
let parent = tcx.local_parent(trait_item.hir_id().owner.def_id);
let hir::Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else {
unreachable!();
};
let (trait_generics, trait_bounds) = match parent_trait.kind {
hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => (generics, supertraits),
hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits),
_ => unreachable!(),
};
if !self.requires_default_supertraits(trait_bounds, trait_generics) {
let self_ty_where_predicates = (parent, trait_item.generics.predicates);
self.add_default_traits_with_filter(
bounds,
tcx.types.self_param,
&[],
Some(self_ty_where_predicates),
trait_item.span,
|tr| tr != hir::LangItem::Sized,
);
}
}
/// Lazily sets `experimental_default_bounds` to true on trait super bounds.
/// See `requires_default_supertraits` for more information.
pub(crate) fn add_default_super_traits(
@ -130,6 +167,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir_generics: &'tcx hir::Generics<'tcx>,
span: Span,
) {
if !self.tcx().sess.opts.unstable_opts.experimental_default_bounds {
return;
}
assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias));
if self.requires_default_supertraits(hir_bounds, hir_generics) {
let self_ty_where_predicates = (trait_def_id, hir_generics.predicates);
@ -263,11 +304,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
seen_unbound = true;
}
let emit_relax_err = || {
let unbound_traits =
match self.tcx().sess.opts.unstable_opts.experimental_default_bounds {
true => "`?Sized` and `experimental_default_bounds`",
false => "`?Sized`",
};
let unbound_traits = match tcx.sess.opts.unstable_opts.experimental_default_bounds {
true => "`?Sized` and `experimental_default_bounds`",
false => "`?Sized`",
};
// There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`.
tcx.dcx().span_err(
unbound.span,

View file

@ -16,6 +16,7 @@ use smallvec::{SmallVec, smallvec};
use tracing::{debug, instrument};
use super::HirTyLowerer;
use crate::errors::SelfInTypeAlias;
use crate::hir_ty_lowering::{
GenericArgCountMismatch, GenericArgCountResult, PredicateFilter, RegionInferReason,
};
@ -125,6 +126,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// ```
let mut projection_bounds = FxIndexMap::default();
for (proj, proj_span) in elaborated_projection_bounds {
let proj = proj.map_bound(|mut b| {
if let Some(term_ty) = &b.term.as_type() {
let references_self = term_ty.walk().any(|arg| arg == dummy_self.into());
if references_self {
// With trait alias and type alias combined, type resolver
// may not be able to catch all illegal `Self` usages (issue 139082)
let guar = tcx.dcx().emit_err(SelfInTypeAlias { span });
b.term = replace_dummy_self_with_error(tcx, b.term, guar);
}
}
b
});
let key = (
proj.skip_binder().projection_term.def_id,
tcx.anonymize_bound_vars(

View file

@ -4,7 +4,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ObligationCause, WellFormedLoc};
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypingMode, fold_regions};
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode, fold_regions};
use rustc_span::def_id::LocalDefId;
use rustc_trait_selection::traits::{self, ObligationCtxt};
use tracing::debug;
@ -77,6 +77,15 @@ fn diagnostic_hir_wf_check<'tcx>(
let tcx_ty = fold_regions(self.tcx, tcx_ty, |r, _| {
if r.is_bound() { self.tcx.lifetimes.re_erased } else { r }
});
// We may be checking the WFness of a type in an opaque with a non-lifetime bound.
// Perhaps we could rebind all the escaping bound vars, but they're coming from
// arbitrary debruijn indices and aren't particularly important anyways, since they
// are only coming from `feature(non_lifetime_binders)` anyways.
if tcx_ty.has_escaping_bound_vars() {
return;
}
let cause = traits::ObligationCause::new(
ty.span,
self.def_id,

View file

@ -24,8 +24,8 @@ pub(super) fn infer_predicates(
// If new predicates were added then we need to re-calculate
// all crates since there could be new implied predicates.
loop {
let mut predicates_added = false;
for i in 0.. {
let mut predicates_added = vec![];
// Visit all the crates and infer predicates
for id in tcx.hir_free_items() {
@ -83,14 +83,27 @@ pub(super) fn infer_predicates(
.get(&item_did.to_def_id())
.map_or(0, |p| p.as_ref().skip_binder().len());
if item_required_predicates.len() > item_predicates_len {
predicates_added = true;
predicates_added.push(item_did);
global_inferred_outlives
.insert(item_did.to_def_id(), ty::EarlyBinder::bind(item_required_predicates));
}
}
if !predicates_added {
if predicates_added.is_empty() {
// We've reached a fixed point.
break;
} else if !tcx.recursion_limit().value_within_limit(i) {
let msg = if let &[id] = &predicates_added[..] {
format!("overflow computing implied lifetime bounds for `{}`", tcx.def_path_str(id),)
} else {
"overflow computing implied lifetime bounds".to_string()
};
tcx.dcx()
.struct_span_fatal(
predicates_added.iter().map(|id| tcx.def_span(*id)).collect::<Vec<_>>(),
msg,
)
.emit();
}
}

View file

@ -146,7 +146,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
fn is_free_region(region: Region<'_>) -> bool {
// First, screen for regions that might appear in a type header.
match *region {
match region.kind() {
// These correspond to `T: 'a` relationships:
//
// struct Foo<'a, T> {

View file

@ -428,7 +428,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
region: ty::Region<'tcx>,
variance: VarianceTermPtr<'a>,
) {
match *region {
match region.kind() {
ty::ReEarlyParam(ref data) => {
self.add_constraint(current, data.index, variance);
}

View file

@ -44,13 +44,13 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
return &[];
}
match tcx.def_kind(item_def_id) {
let kind = tcx.def_kind(item_def_id);
match kind {
DefKind::Fn
| DefKind::AssocFn
| DefKind::Enum
| DefKind::Struct
| DefKind::Union
| DefKind::Variant
| DefKind::Ctor(..) => {
// These are inferred.
let crate_map = tcx.crate_variances(());
@ -89,7 +89,11 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
}
// Variance not relevant.
span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item");
span_bug!(
tcx.def_span(item_def_id),
"asked to compute variance for {}",
kind.descr(item_def_id.to_def_id())
);
}
#[derive(Debug, Copy, Clone)]

View file

@ -960,12 +960,16 @@ impl<'a> State<'a> {
fn print_local(
&mut self,
super_: bool,
init: Option<&hir::Expr<'_>>,
els: Option<&hir::Block<'_>>,
decl: impl Fn(&mut Self),
) {
self.space_if_not_bol();
self.ibox(INDENT_UNIT);
if super_ {
self.word_nbsp("super");
}
self.word_nbsp("let");
self.ibox(INDENT_UNIT);
@ -995,7 +999,9 @@ impl<'a> State<'a> {
self.maybe_print_comment(st.span.lo());
match st.kind {
hir::StmtKind::Let(loc) => {
self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc));
self.print_local(loc.super_.is_some(), loc.init, loc.els, |this| {
this.print_local_decl(loc)
});
}
hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)),
hir::StmtKind::Expr(expr) => {
@ -1488,7 +1494,7 @@ impl<'a> State<'a> {
// Print `let _t = $init;`:
let temp = Ident::from_str("_t");
self.print_local(Some(init), None, |this| this.print_ident(temp));
self.print_local(false, Some(init), None, |this| this.print_ident(temp));
self.word(";");
// Print `_t`:
@ -1868,6 +1874,7 @@ impl<'a> State<'a> {
// Pat isn't normalized, but the beauty of it
// is that it doesn't matter
match pat.kind {
PatKind::Missing => unreachable!(),
PatKind::Wild => self.word("_"),
PatKind::Never => self.word("!"),
PatKind::Binding(BindingMode(by_ref, mutbl), _, ident, sub) => {

View file

@ -5,7 +5,7 @@ use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir_analysis::check::{check_function_signature, forbid_intrinsic_abi};
use rustc_hir_analysis::check::check_function_signature;
use rustc_infer::infer::RegionVariableOrigin;
use rustc_infer::traits::WellFormedLoc;
use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
@ -50,8 +50,6 @@ pub(super) fn check_fn<'a, 'tcx>(
let span = body.value.span;
forbid_intrinsic_abi(tcx, span, fn_sig.abi);
GatherLocalsVisitor::new(fcx).visit_body(body);
// C-variadic fns also have a `VaList` input that's not listed in `fn_sig`

View file

@ -970,7 +970,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
// Normalize only after registering in `user_provided_sigs`.
self.normalize(self.tcx.hir_span(hir_id), result)
self.normalize(self.tcx.def_span(expr_def_id), result)
}
/// Invoked when we are translating the coroutine that results

View file

@ -37,7 +37,6 @@
use std::ops::Deref;
use rustc_abi::ExternAbi;
use rustc_attr_parsing::InlineAttr;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, struct_span_code_err};
@ -1240,10 +1239,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
// Intrinsics are not coercible to function pointers.
if a_sig.abi() == ExternAbi::RustIntrinsic || b_sig.abi() == ExternAbi::RustIntrinsic {
return Err(TypeError::IntrinsicCast);
}
// The signature must match.
let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
let sig = self

View file

@ -865,10 +865,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty`
vec![(
ty_ref.1.ty.span.shrink_to_lo(),
format!(
"{}mut ",
if ty_ref.0.ident.span.lo() == ty_ref.0.ident.span.hi() { "" } else { " " },
),
format!("{}mut ", if ty_ref.0.ident.span.is_empty() { "" } else { " " },),
)]
};
sugg.extend([

View file

@ -482,7 +482,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// All of these constitute a read, or match on something that isn't `!`,
// which would require a `NeverToAny` coercion.
hir::PatKind::Binding(_, _, _, _)
hir::PatKind::Missing
| hir::PatKind::Binding(_, _, _, _)
| hir::PatKind::Struct(_, _, _)
| hir::PatKind::TupleStruct(_, _, _)
| hir::PatKind::Tuple(_, _)
@ -2204,8 +2205,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let fields = listify(&missing_mandatory_fields, |f| format!("`{f}`")).unwrap();
self.dcx()
.struct_span_err(
span.shrink_to_hi(),
format!("missing mandatory field{s} {fields}"),
span.shrink_to_lo(),
format!("missing field{s} {fields} in initializer"),
)
.with_span_label(
span.shrink_to_lo(),
"fields that do not have a defaulted value must be provided explicitly",
)
.emit();
return;

View file

@ -611,6 +611,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
for pat in pats {
self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
match &pat.kind {
PatKind::Missing => unreachable!(),
PatKind::Binding(.., opt_sub_pat) => {
// If the opt_sub_pat is None, then the binding does not count as
// a wildcard for the purpose of borrowing discr.
@ -1832,6 +1833,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
| PatKind::Expr(..)
| PatKind::Range(..)
| PatKind::Never
| PatKind::Missing
| PatKind::Wild
| PatKind::Err(_) => {
// always ok

View file

@ -43,7 +43,7 @@ pub(super) struct Declaration<'a> {
impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> {
fn from(local: &'a hir::LetStmt<'a>) -> Self {
let hir::LetStmt { hir_id, pat, ty, span, init, els, source: _ } = *local;
let hir::LetStmt { hir_id, super_: _, pat, ty, span, init, els, source: _ } = *local;
Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } }
}
}

View file

@ -117,7 +117,7 @@ fn typeck_with_inspect<'tcx>(
let id = tcx.local_def_id_to_hir_id(def_id);
let node = tcx.hir_node(id);
let span = tcx.hir_span(id);
let span = tcx.def_span(def_id);
// Figure out what primary body this item has.
let body_id = node.body_id().unwrap_or_else(|| {

View file

@ -25,6 +25,7 @@ use rustc_middle::bug;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type};
use rustc_middle::ty::print::{
PrintTraitRefExt as _, with_crate_prefix, with_forced_trimmed_paths,
with_no_visible_paths_if_doc_hidden,
};
use rustc_middle::ty::{self, GenericArgKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::def_id::DefIdSet;
@ -3328,7 +3329,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let path_strings = candidates.iter().map(|trait_did| {
format!(
"{prefix}{}{postfix}\n",
with_crate_prefix!(self.tcx.def_path_str(*trait_did)),
with_no_visible_paths_if_doc_hidden!(with_crate_prefix!(
self.tcx.def_path_str(*trait_did)
)),
)
});
@ -3336,7 +3339,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let parent_did = parent_map.get(trait_did).unwrap();
format!(
"{prefix}{}::*{postfix} // trait {}\n",
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
with_no_visible_paths_if_doc_hidden!(with_crate_prefix!(
self.tcx.def_path_str(*parent_did)
)),
self.tcx.item_name(*trait_did),
)
});

View file

@ -341,7 +341,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let ty = match pat.kind {
PatKind::Wild | PatKind::Err(_) => expected,
PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
// We allow any type here; we ensure that the type is uninhabited during match checking.
PatKind::Never => expected,
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
@ -505,9 +505,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
// Ref patterns are complicated, we handle them in `check_pat_ref`.
PatKind::Ref(..) => AdjustMode::Pass,
PatKind::Ref(..)
// No need to do anything on a missing pattern.
| PatKind::Missing
// A `_` pattern works with any expected type, so there's no need to do anything.
PatKind::Wild
| PatKind::Wild
// A malformed pattern doesn't have an expected type, so let's just accept any type.
| PatKind::Err(_)
// Bindings also work with whatever the expected type is,
@ -1032,7 +1034,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| PatKind::Tuple(..)
| PatKind::Slice(..) => "binding",
PatKind::Wild
PatKind::Missing
| PatKind::Wild
| PatKind::Never
| PatKind::Binding(..)
| PatKind::Box(..)

View file

@ -159,7 +159,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse {
) -> ty::Region<'tcx> {
let infcx = canonicalizer.infcx.unwrap();
if let ty::ReVar(vid) = *r {
if let ty::ReVar(vid) = r.kind() {
r = infcx
.inner
.borrow_mut()
@ -171,7 +171,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse {
);
};
match *r {
match r.kind() {
ty::ReLateParam(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyParam(..) => r,
ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
@ -227,7 +227,7 @@ impl CanonicalizeMode for CanonicalizeUserTypeAnnotation {
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
match *r {
match r.kind() {
ty::ReEarlyParam(_)
| ty::ReLateParam(_)
| ty::ReErased
@ -321,7 +321,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
match r.kind() {
ty::ReBound(index, ..) => {
if index >= self.binder_index {
bug!("escaping late-bound region during canonicalization");

View file

@ -432,7 +432,7 @@ impl<'tcx> InferCtxt<'tcx> {
}
GenericArgKind::Lifetime(result_value) => {
// e.g., here `result_value` might be `'?1` in the example above...
if let ty::ReBound(debruijn, br) = *result_value {
if let ty::ReBound(debruijn, br) = result_value.kind() {
// ... in which case we would set `canonical_vars[0]` to `Some('static)`.
// We only allow a `ty::INNERMOST` index in generic parameters.

View file

@ -109,7 +109,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
match r.kind() {
ty::ReBound(..) => {
// leave bound regions alone
r

View file

@ -218,7 +218,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
true
}
VarValue::Value(cur_region) => {
match *cur_region {
match cur_region.kind() {
// If this empty region is from a universe that can name the
// placeholder universe, then the LUB is the Placeholder region
// (which is the cur_region). Otherwise, the LUB is the Static
@ -310,7 +310,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
match *b_data {
VarValue::Empty(empty_ui) => {
let lub = match *a_region {
let lub = match a_region.kind() {
RePlaceholder(placeholder) => {
// If this empty region is from a universe that can
// name the placeholder, then the placeholder is
@ -350,7 +350,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// tighter bound than `'static`.
//
// (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.)
if let ty::RePlaceholder(p) = *lub
if let ty::RePlaceholder(p) = lub.kind()
&& b_universe.cannot_name(p.universe)
{
lub = self.tcx().lifetimes.re_static;
@ -377,7 +377,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
a_ui.min(b_ui) == b_ui
}
(VarValue::Value(a), VarValue::Empty(_)) => {
match *a {
match a.kind() {
// this is always on an error path,
// so it doesn't really matter if it's shorter or longer than an empty region
ReError(_) => false,
@ -410,7 +410,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
}
(VarValue::Empty(a_ui), VarValue::Value(b)) => {
match *b {
match b.kind() {
// this is always on an error path,
// so it doesn't really matter if it's shorter or longer than an empty region
ReError(_) => false,
@ -479,7 +479,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
/// term "concrete regions").
#[instrument(level = "trace", skip(self), ret)]
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
match (*a, *b) {
match (a.kind(), b.kind()) {
(ReBound(..), _) | (_, ReBound(..)) | (ReErased, _) | (_, ReErased) => {
bug!("cannot relate region: LUB({:?}, {:?})", a, b);
}
@ -725,7 +725,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// SubSupConflict(ReLateParam, ReLateParam) when reporting error, and so
// the user will more likely get a specific suggestion.
fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 {
match *x.region {
match x.region.kind() {
ReEarlyParam(_) => 0,
ReLateParam(_) => 1,
_ => 2,
@ -737,7 +737,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
let node_universe = self.var_infos[node_idx].universe;
for lower_bound in &lower_bounds {
let effective_lower_bound = if let ty::RePlaceholder(p) = *lower_bound.region {
let effective_lower_bound = if let ty::RePlaceholder(p) = lower_bound.region.kind() {
if node_universe.cannot_name(p.universe) {
self.tcx().lifetimes.re_static
} else {
@ -785,7 +785,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
.expect("lower_vid_bounds should at least include `node_idx`");
for upper_bound in &upper_bounds {
if let ty::RePlaceholder(p) = *upper_bound.region {
if let ty::RePlaceholder(p) = upper_bound.region.kind() {
if min_universe.cannot_name(p.universe) {
let origin = self.var_infos[node_idx].origin;
errors.push(RegionResolutionError::UpperBoundUniverseConflict(
@ -913,7 +913,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
generic_ty: Ty<'tcx>,
min: ty::Region<'tcx>,
) -> bool {
if let ty::ReError(_) = *min {
if let ty::ReError(_) = min.kind() {
return true;
}
@ -931,18 +931,18 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
VerifyBound::OutlivedBy(r) => {
let a = match *min {
let a = match min.kind() {
ty::ReVar(rid) => var_values.values[rid],
_ => VarValue::Value(min),
};
let b = match **r {
let b = match r.kind() {
ty::ReVar(rid) => var_values.values[rid],
_ => VarValue::Value(*r),
};
self.sub_region_values(a, b)
}
VerifyBound::IsEmpty => match *min {
VerifyBound::IsEmpty => match min.kind() {
ty::ReVar(rid) => match var_values.values[rid] {
VarValue::ErrorValue => false,
VarValue::Empty(_) => true,
@ -989,7 +989,7 @@ impl<'tcx> LexicalRegionResolutions<'tcx> {
tcx: TyCtxt<'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
let result = match *r {
let result = match r.kind() {
ty::ReVar(rid) => match self.values[rid] {
VarValue::Empty(_) => r,
VarValue::Value(r) => r,

View file

@ -69,7 +69,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
OutlivesBound::RegionSubRegion(r_a, r_b) => match (*r_a, *r_b) {
OutlivesBound::RegionSubRegion(r_a, r_b) => match (r_a.kind(), r_b.kind()) {
(
ty::ReStatic | ty::ReEarlyParam(_) | ty::ReLateParam(_),
ty::ReStatic | ty::ReEarlyParam(_) | ty::ReLateParam(_),

View file

@ -29,7 +29,7 @@ where
}
fn visit_region(&mut self, r: ty::Region<'tcx>) {
match *r {
match r.kind() {
// ignore bound regions, keep visiting
ty::ReBound(_, _) => {}
_ => (self.op)(r),

View file

@ -155,7 +155,7 @@ impl<'a, 'tcx> LeakCheck<'a, 'tcx> {
self.scc_universes[scc].take_min(universe, *region);
// Detect those SCCs that directly contain a placeholder
if let ty::RePlaceholder(placeholder) = **region {
if let ty::RePlaceholder(placeholder) = region.kind() {
if self.outer_universe.cannot_name(placeholder.universe) {
// Update `scc_placeholders` to account for the fact that `P: S` must hold.
match self.scc_placeholders[scc] {

View file

@ -463,7 +463,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
// cannot add constraints once regions are resolved
debug!("origin = {:#?}", origin);
match (*sub, *sup) {
match (sub.kind(), sup.kind()) {
(ReBound(..), _) | (_, ReBound(..)) => {
span_bug!(origin.span(), "cannot relate bound region: {:?} <= {:?}", sub, sup);
}
@ -595,7 +595,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
}
pub fn universe(&mut self, region: Region<'tcx>) -> ty::UniverseIndex {
match *region {
match region.kind() {
ty::ReStatic
| ty::ReErased
| ty::ReLateParam(..)

View file

@ -571,7 +571,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
) -> RelateResult<'tcx, ty::Region<'tcx>> {
assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
match *r {
match r.kind() {
// Never make variables for regions bound within the type itself,
// nor for erased regions.
ty::ReBound(..) | ty::ReErased => {

View file

@ -89,7 +89,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticRegionResolver<'a, 'tcx
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
match r.kind() {
ty::ReVar(vid) => self
.infcx
.inner
@ -153,7 +153,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for FullTypeResolver<'a, 'tcx> {
}
fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> {
match *r {
match r.kind() {
ty::ReVar(_) => Ok(self
.infcx
.lexical_region_resolutions

View file

@ -955,7 +955,9 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
// Run unsafety check because it's responsible for stealing and
// deallocating THIR.
tcx.ensure_ok().check_unsafety(def_id);
tcx.ensure_ok().mir_borrowck(def_id)
if !tcx.is_typeck_child(def_id.to_def_id()) {
tcx.ensure_ok().mir_borrowck(def_id)
}
});
});
sess.time("MIR_effect_checking", || {

View file

@ -779,21 +779,19 @@ impl EarlyLintPass for AnonymousParameters {
}
if let ast::AssocItemKind::Fn(box Fn { ref sig, .. }) = it.kind {
for arg in sig.decl.inputs.iter() {
if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind {
if ident.name == kw::Empty {
let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span);
if let ast::PatKind::Missing = arg.pat.kind {
let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span);
let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {
(snip.as_str(), Applicability::MachineApplicable)
} else {
("<type>", Applicability::HasPlaceholders)
};
cx.emit_span_lint(
ANONYMOUS_PARAMETERS,
arg.pat.span,
BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip },
);
}
let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {
(snip.as_str(), Applicability::MachineApplicable)
} else {
("<type>", Applicability::HasPlaceholders)
};
cx.emit_span_lint(
ANONYMOUS_PARAMETERS,
arg.pat.span,
BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip },
);
}
}
}
@ -1990,7 +1988,7 @@ impl ExplicitOutlivesRequirements {
inferred_outlives
.filter_map(|(clause, _)| match clause.kind().skip_binder() {
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a {
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a.kind() {
ty::ReEarlyParam(ebr)
if item_generics.region_param(ebr, tcx).def_id == lifetime.to_def_id() =>
{
@ -2040,7 +2038,7 @@ impl ExplicitOutlivesRequirements {
let is_inferred = match tcx.named_bound_var(lifetime.hir_id) {
Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives
.iter()
.any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { item_generics.region_param(ebr, tcx).def_id == def_id.to_def_id() })),
.any(|r| matches!(r.kind(), ty::ReEarlyParam(ebr) if { item_generics.region_param(ebr, tcx).def_id == def_id.to_def_id() })),
_ => false,
};

View file

@ -461,7 +461,7 @@ fn extract_def_id_from_arg<'tcx>(
arg: ty::GenericArg<'tcx>,
) -> DefId {
match arg.unpack() {
ty::GenericArgKind::Lifetime(re) => match *re {
ty::GenericArgKind::Lifetime(re) => match re.kind() {
ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id,
ty::ReBound(
_,
@ -530,7 +530,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for FunctionalVariances<'tcx> {
a: ty::Region<'tcx>,
_: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
let def_id = match *a {
let def_id = match a.kind() {
ty::ReEarlyParam(ebr) => self.generics.region_param(ebr, self.tcx).def_id,
ty::ReBound(
_,

View file

@ -1,10 +1,11 @@
use rustc_ast::attr::AttributeExt;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::unord::UnordSet;
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
use rustc_feature::{Features, GateIssue};
use rustc_hir::HirId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{CRATE_HIR_ID, HirId};
use rustc_index::IndexVec;
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
@ -115,12 +116,11 @@ impl LintLevelSets {
}
}
fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
let store = unerased_lint_store(&tcx.sess);
let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID);
let map = tcx.shallow_lint_levels_on(rustc_hir::CRATE_OWNER_ID);
let dont_need_to_run: FxIndexSet<LintId> = store
let mut dont_need_to_run: FxHashSet<LintId> = store
.get_lints()
.into_iter()
.filter(|lint| {
@ -129,24 +129,31 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
lint.future_incompatible.is_some_and(|fut| fut.reason.has_future_breakage());
!has_future_breakage && !lint.eval_always
})
.filter_map(|lint| {
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
if matches!(lint_level.level, Level::Allow)
|| (matches!(lint_level.src, LintLevelSource::Default))
&& lint.default_level(tcx.sess.edition()) == Level::Allow
{
Some(LintId::of(lint))
} else {
None
}
.filter(|lint| {
let lint_level =
root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
// Only include lints that are allowed at crate root or by default.
matches!(lint_level.level, Level::Allow)
|| (matches!(lint_level.src, LintLevelSource::Default)
&& lint.default_level(tcx.sess.edition()) == Level::Allow)
})
.map(|lint| LintId::of(*lint))
.collect();
let mut visitor = LintLevelMaximum { tcx, dont_need_to_run };
visitor.process_opts();
tcx.hir_walk_attributes(&mut visitor);
for owner in tcx.hir_crate_items(()).owners() {
let map = tcx.shallow_lint_levels_on(owner);
visitor.dont_need_to_run
// All lints that appear with a non-allow level must be run.
for (_, specs) in map.specs.iter() {
for (lint, level_and_source) in specs.iter() {
if !matches!(level_and_source.level, Level::Allow) {
dont_need_to_run.remove(lint);
}
}
}
}
dont_need_to_run.into()
}
#[instrument(level = "trace", skip(tcx), ret)]
@ -340,76 +347,6 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
}
}
/// Visitor with the only function of visiting every item-like in a crate and
/// computing the highest level that every lint gets put to.
///
/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item
/// uses #[warn(lint)], this visitor will set that lint level as `Warn`
struct LintLevelMaximum<'tcx> {
tcx: TyCtxt<'tcx>,
/// The actual list of detected lints.
dont_need_to_run: FxIndexSet<LintId>,
}
impl<'tcx> LintLevelMaximum<'tcx> {
fn process_opts(&mut self) {
let store = unerased_lint_store(self.tcx.sess);
for (lint_group, level) in &self.tcx.sess.opts.lint_opts {
if *level != Level::Allow {
let Ok(lints) = store.find_lints(lint_group) else {
return;
};
for lint in lints {
self.dont_need_to_run.swap_remove(&lint);
}
}
}
}
}
impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
type NestedFilter = nested_filter::All;
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.tcx
}
/// FIXME(blyxyas): In a future revision, we should also graph #![allow]s,
/// but that is handled with more care
fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) {
if matches!(
Level::from_attr(attribute),
Some((Level::Warn | Level::Deny | Level::Forbid | Level::Expect | Level::ForceWarn, _))
) {
let store = unerased_lint_store(self.tcx.sess);
// Lint attributes are always a metalist inside a
// metalist (even with just one lint).
let Some(meta_item_list) = attribute.meta_item_list() else { return };
for meta_list in meta_item_list {
// Convert Path to String
let Some(meta_item) = meta_list.meta_item() else { return };
let ident: &str = &meta_item
.path
.segments
.iter()
.map(|segment| segment.ident.as_str())
.collect::<Vec<&str>>()
.join("::");
let Ok(lints) = store.find_lints(
// Lint attributes can only have literals
ident,
) else {
return;
};
for lint in lints {
self.dont_need_to_run.swap_remove(&lint);
}
}
}
}
}
pub struct LintLevelsBuilder<'s, P> {
sess: &'s Session,
features: &'s Features,

View file

@ -513,7 +513,7 @@ impl Subdiagnostic for BuiltinClashingExternSub<'_> {
expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false);
let mut found_str = DiagStyledString::new();
found_str.push(self.found.fn_sig(self.tcx).to_string(), true);
diag.note_expected_found(&"", expected_str, &"", found_str);
diag.note_expected_found("", expected_str, "", found_str);
}
}

View file

@ -1201,7 +1201,8 @@ impl EarlyLintPass for UnusedParens {
// Do not lint on `(..)` as that will result in the other arms being useless.
Paren(_)
// The other cases do not contain sub-patterns.
| Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {},
| Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
| Path(..) | Err(_) => {},
// These are list-like patterns; parens can always be removed.
TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
self.check_unused_parens_pat(cx, p, false, false, keep_space);

View file

@ -207,7 +207,7 @@ impl<'tcx> Collector<'tcx> {
let sess = self.tcx.sess;
if matches!(abi, ExternAbi::Rust | ExternAbi::RustIntrinsic) {
if matches!(abi, ExternAbi::Rust) {
return;
}

View file

@ -1099,7 +1099,6 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def
DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Variant
| DefKind::OpaqueTy
| DefKind::Fn
| DefKind::Ctor(..)
@ -1109,6 +1108,7 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def
matches!(tcx.opt_rpitit_info(def_id), Some(ty::ImplTraitInTraitData::Trait { .. }))
}
DefKind::Mod
| DefKind::Variant
| DefKind::Field
| DefKind::AssocConst
| DefKind::TyParam

Some files were not shown because too many files have changed in this diff Show more