Merge from rustc
This commit is contained in:
commit
f69ea4d82f
872 changed files with 7959 additions and 5020 deletions
|
|
@ -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
|
||||
|
|
|
|||
61
Cargo.lock
61
Cargo.lock
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
®ioncx,
|
||||
&opt_closure_req,
|
||||
&concrete_opaque_types,
|
||||
diags_buffer,
|
||||
);
|
||||
nll::dump_annotation(&infcx, body, ®ioncx, &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(..)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
101
compiler/rustc_borrowck/src/root_cx.rs
Normal file
101
compiler/rustc_borrowck/src/root_cx.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
170
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/unused.rs
Normal file
170
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/unused.rs
Normal 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)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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![],
|
||||
|
|
|
|||
|
|
@ -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(); } }
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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([
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 } }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(|| {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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(..)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(_),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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] {
|
||||
|
|
|
|||
|
|
@ -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(..)
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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", || {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
_,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue