Merge ref '47611e1604' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh.
Upstream ref: rust-lang/rust@47611e1604
Filtered ref: rust-lang/miri@9e16c92d15
Upstream diff: f889772d65...47611e1604
This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
commit
dce9791ba7
1178 changed files with 22539 additions and 23491 deletions
|
|
@ -10,7 +10,7 @@ the Zulip stream is the best place to *ask* for help.
|
|||
|
||||
Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc
|
||||
Development][rustc-dev-guide], commonly known as the [rustc-dev-guide]. Documentation for the
|
||||
standard library in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide].
|
||||
standard library is in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide].
|
||||
|
||||
## Making changes to subtrees and submodules
|
||||
|
||||
|
|
|
|||
31
Cargo.lock
31
Cargo.lock
|
|
@ -3539,6 +3539,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_hir",
|
||||
|
|
@ -3571,7 +3572,6 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_graphviz",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
|
|
@ -3687,7 +3687,6 @@ dependencies = [
|
|||
"serde_json",
|
||||
"smallvec",
|
||||
"tempfile",
|
||||
"thin-vec",
|
||||
"thorin-dwp",
|
||||
"tracing",
|
||||
"wasm-encoder 0.219.2",
|
||||
|
|
@ -3704,7 +3703,6 @@ dependencies = [
|
|||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
|
|
@ -3771,7 +3769,6 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_borrowck",
|
||||
"rustc_codegen_ssa",
|
||||
"rustc_const_eval",
|
||||
"rustc_data_structures",
|
||||
|
|
@ -3791,13 +3788,11 @@ dependencies = [
|
|||
"rustc_mir_build",
|
||||
"rustc_mir_transform",
|
||||
"rustc_parse",
|
||||
"rustc_passes",
|
||||
"rustc_public",
|
||||
"rustc_resolve",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"rustc_trait_selection",
|
||||
"serde_json",
|
||||
"shlex",
|
||||
"tracing",
|
||||
|
|
@ -3892,19 +3887,6 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_fluent_macro"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.11.5",
|
||||
"fluent-bundle",
|
||||
"fluent-syntax",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.110",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_fs_util"
|
||||
version = "0.0.0"
|
||||
|
|
@ -3959,7 +3941,6 @@ dependencies = [
|
|||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
|
|
@ -4148,7 +4129,6 @@ dependencies = [
|
|||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
|
|
@ -4286,7 +4266,6 @@ dependencies = [
|
|||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
|
|
@ -4388,7 +4367,6 @@ dependencies = [
|
|||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_index",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
|
|
@ -4421,7 +4399,6 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_expand",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
|
|
@ -4515,6 +4492,7 @@ name = "rustc_query_impl"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"measureme",
|
||||
"rustc_abi",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hashes",
|
||||
|
|
@ -4524,7 +4502,9 @@ dependencies = [
|
|||
"rustc_middle",
|
||||
"rustc_query_system",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_thread_pool",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -4570,7 +4550,6 @@ dependencies = [
|
|||
"rustc_macros",
|
||||
"rustc_metadata",
|
||||
"rustc_middle",
|
||||
"rustc_query_system",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"smallvec",
|
||||
|
|
@ -4724,7 +4703,6 @@ dependencies = [
|
|||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_infer",
|
||||
"rustc_macros",
|
||||
|
|
@ -5586,7 +5564,6 @@ dependencies = [
|
|||
"build_helper",
|
||||
"cargo_metadata 0.21.0",
|
||||
"clap",
|
||||
"fluent-syntax",
|
||||
"globset",
|
||||
"ignore",
|
||||
"miropt-test-tools",
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ itself back on after some time).
|
|||
|
||||
### MSVC
|
||||
|
||||
MSVC builds of Rust additionally requires an installation of:
|
||||
MSVC builds of Rust additionally require an installation of:
|
||||
|
||||
- Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older
|
||||
Visual Studio versions such as 2019 *may* work but aren't actively tested.
|
||||
|
|
|
|||
17
RELEASES.md
17
RELEASES.md
|
|
@ -1,3 +1,12 @@
|
|||
Version 1.93.1 (2026-02-12)
|
||||
===========================
|
||||
|
||||
<a id="1.93.1"></a>
|
||||
|
||||
- [Don't try to recover keyword as non-keyword identifier](https://github.com/rust-lang/rust/pull/150590), fixing an ICE that especially [affected rustfmt](https://github.com/rust-lang/rustfmt/issues/6739).
|
||||
- [Fix `clippy::panicking_unwrap` false-positive on field access with implicit deref](https://github.com/rust-lang/rust-clippy/pull/16196).
|
||||
- [Revert "Update wasm-related dependencies in CI"](https://github.com/rust-lang/rust/pull/152259), fixing file descriptor leaks on the `wasm32-wasip2` target.
|
||||
|
||||
Version 1.93.0 (2026-01-22)
|
||||
==========================
|
||||
|
||||
|
|
@ -1546,7 +1555,7 @@ Compatibility Notes
|
|||
- [Check well-formedness of the source type's signature in fn pointer casts.](https://github.com/rust-lang/rust/pull/129021) This partly closes a soundness hole that comes when casting a function item to function pointer
|
||||
- [Use equality instead of subtyping when resolving type dependent paths.](https://github.com/rust-lang/rust/pull/129073)
|
||||
- Linking on macOS now correctly includes Rust's default deployment target. Due to a linker bug, you might have to pass `MACOSX_DEPLOYMENT_TARGET` or fix your `#[link]` attributes to point to the correct frameworks. See <https://github.com/rust-lang/rust/pull/129369>.
|
||||
- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previous did not have any effect.](https://github.com/rust-lang/rust/pull/129422)
|
||||
- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previously did not have any effect.](https://github.com/rust-lang/rust/pull/129422)
|
||||
- The future incompatibility lint `deprecated_cfg_attr_crate_type_name` [has been made into a hard error](https://github.com/rust-lang/rust/pull/129670). It was used to deny usage of `#![crate_type]` and `#![crate_name]` attributes in `#![cfg_attr]`, which required a hack in the compiler to be able to change the used crate type and crate name after cfg expansion.
|
||||
Users can use `--crate-type` instead of `#![cfg_attr(..., crate_type = "...")]` and `--crate-name` instead of `#![cfg_attr(..., crate_name = "...")]` when running `rustc`/`cargo rustc` on the command line.
|
||||
Use of those two attributes outside of `#![cfg_attr]` continue to be fully supported.
|
||||
|
|
@ -1722,7 +1731,7 @@ Cargo
|
|||
Compatibility Notes
|
||||
-------------------
|
||||
- We now [disallow setting some built-in cfgs via the command-line](https://github.com/rust-lang/rust/pull/126158) with the newly added [`explicit_builtin_cfgs_in_flags`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#explicit-builtin-cfgs-in-flags) lint in order to prevent incoherent state, eg. `windows` cfg active but target is Linux based. The appropriate [`rustc` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html) should be used instead.
|
||||
- The standard library has a new implementation of `binary_search` which is significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation.
|
||||
- The standard library has a new implementation of `binary_search` which significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation.
|
||||
- [illumos/Solaris now sets `MSG_NOSIGNAL` when writing to sockets](https://github.com/rust-lang/rust/pull/128259). This avoids killing the process with SIGPIPE when writing to a closed socket, which matches the existing behavior on other UNIX targets.
|
||||
- [Removes a problematic hack that always passed the --whole-archive linker flag for tests, which may cause linker errors for code accidentally relying on it.](https://github.com/rust-lang/rust/pull/128400)
|
||||
- The WebAssembly target features `multivalue` and `reference-types` are now
|
||||
|
|
@ -1872,7 +1881,7 @@ These changes do not affect any public interfaces of Rust, but they represent
|
|||
significant improvements to the performance or internals of rustc and related
|
||||
tools.
|
||||
|
||||
- [Add a Rust-for Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/)
|
||||
- [Add a Rust-for-Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/)
|
||||
|
||||
Version 1.80.1 (2024-08-08)
|
||||
===========================
|
||||
|
|
@ -4510,7 +4519,7 @@ Compatibility Notes
|
|||
saturating to `0` instead][89926]. In the real world the panic happened mostly
|
||||
on platforms with buggy monotonic clock implementations rather than catching
|
||||
programming errors like reversing the start and end times. Such programming
|
||||
errors will now results in `0` rather than a panic.
|
||||
errors will now result in `0` rather than a panic.
|
||||
- In a future release we're planning to increase the baseline requirements for
|
||||
the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love
|
||||
your feedback in [PR #95026][95026].
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ impl Reg {
|
|||
|
||||
reg_ctor!(f32, Float, 32);
|
||||
reg_ctor!(f64, Float, 64);
|
||||
reg_ctor!(f128, Float, 128);
|
||||
}
|
||||
|
||||
impl Reg {
|
||||
|
|
|
|||
|
|
@ -290,7 +290,19 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
/// function call isn't allowed (a.k.a. `va_list`).
|
||||
///
|
||||
/// This function handles transparent types automatically.
|
||||
pub fn pass_indirectly_in_non_rustic_abis<C>(mut self, cx: &C) -> bool
|
||||
pub fn pass_indirectly_in_non_rustic_abis<C>(self, cx: &C) -> bool
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
let base = self.peel_transparent_wrappers(cx);
|
||||
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(base)
|
||||
}
|
||||
|
||||
/// Recursively peel away transparent wrappers, returning the inner value.
|
||||
///
|
||||
/// The return value is not `repr(transparent)` and/or does
|
||||
/// not have a non-1zst field.
|
||||
pub fn peel_transparent_wrappers<C>(mut self, cx: &C) -> Self
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
|
|
@ -300,7 +312,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
self = field;
|
||||
}
|
||||
|
||||
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self)
|
||||
self
|
||||
}
|
||||
|
||||
/// Finds the one field that is not a 1-ZST.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// tidy-alphabetical-start
|
||||
#![cfg_attr(all(feature = "nightly", bootstrap), feature(assert_matches))]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", feature(assert_matches))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
|
||||
#![cfg_attr(feature = "nightly", feature(step_trait))]
|
||||
// tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#![cfg_attr(test, feature(test))]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![doc(test(no_crate_inject, attr(deny(warnings), allow(internal_features))))]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(never_type)]
|
||||
|
|
@ -26,7 +25,7 @@ use std::cell::{Cell, RefCell};
|
|||
use std::marker::PhantomData;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::{cmp, intrinsics, slice};
|
||||
use std::{cmp, hint, slice};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
|
@ -452,7 +451,7 @@ impl DroplessArena {
|
|||
let bytes = align_up(layout.size(), DROPLESS_ALIGNMENT);
|
||||
|
||||
// Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT.
|
||||
unsafe { intrinsics::assume(end == align_down(end, DROPLESS_ALIGNMENT)) };
|
||||
unsafe { hint::assert_unchecked(end == align_down(end, DROPLESS_ALIGNMENT)) };
|
||||
|
||||
if let Some(sub) = end.checked_sub(bytes) {
|
||||
let new_end = align_down(sub, layout.align());
|
||||
|
|
|
|||
|
|
@ -656,11 +656,7 @@ impl Pat {
|
|||
// A tuple pattern `(P0, .., Pn)` can be reparsed as `(T0, .., Tn)`
|
||||
// assuming `T0` to `Tn` are all syntactically valid as types.
|
||||
PatKind::Tuple(pats) => {
|
||||
let mut tys = ThinVec::with_capacity(pats.len());
|
||||
// FIXME(#48994) - could just be collected into an Option<Vec>
|
||||
for pat in pats {
|
||||
tys.push(pat.to_ty()?);
|
||||
}
|
||||
let tys = pats.iter().map(|pat| pat.to_ty()).collect::<Option<ThinVec<_>>>()?;
|
||||
TyKind::Tup(tys)
|
||||
}
|
||||
_ => return None,
|
||||
|
|
@ -3873,27 +3869,44 @@ pub struct ConstItem {
|
|||
pub ident: Ident,
|
||||
pub generics: Generics,
|
||||
pub ty: Box<Ty>,
|
||||
pub rhs: Option<ConstItemRhs>,
|
||||
pub rhs_kind: ConstItemRhsKind,
|
||||
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub enum ConstItemRhs {
|
||||
TypeConst(AnonConst),
|
||||
Body(Box<Expr>),
|
||||
pub enum ConstItemRhsKind {
|
||||
Body { rhs: Option<Box<Expr>> },
|
||||
TypeConst { rhs: Option<AnonConst> },
|
||||
}
|
||||
|
||||
impl ConstItemRhs {
|
||||
pub fn span(&self) -> Span {
|
||||
self.expr().span
|
||||
impl ConstItemRhsKind {
|
||||
pub fn new_body(rhs: Box<Expr>) -> Self {
|
||||
Self::Body { rhs: Some(rhs) }
|
||||
}
|
||||
|
||||
pub fn expr(&self) -> &Expr {
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
Some(self.expr()?.span)
|
||||
}
|
||||
|
||||
pub fn expr(&self) -> Option<&Expr> {
|
||||
match self {
|
||||
ConstItemRhs::TypeConst(anon_const) => &anon_const.value,
|
||||
ConstItemRhs::Body(expr) => expr,
|
||||
Self::Body { rhs: Some(body) } => Some(&body),
|
||||
Self::TypeConst { rhs: Some(anon) } => Some(&anon.value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_expr(&self) -> bool {
|
||||
match self {
|
||||
Self::Body { rhs: Some(_) } => true,
|
||||
Self::TypeConst { rhs: Some(_) } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_type_const(&self) -> bool {
|
||||
matches!(self, &Self::TypeConst { .. })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
Const,
|
||||
ConstBlockItem,
|
||||
ConstItem,
|
||||
ConstItemRhs,
|
||||
ConstItemRhsKind,
|
||||
Defaultness,
|
||||
Delegation,
|
||||
DelegationMac,
|
||||
|
|
|
|||
|
|
@ -966,14 +966,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
|
||||
this.arena.alloc(this.expr(gen_future_span, expr_break))
|
||||
});
|
||||
self.arm(ready_pat, break_x)
|
||||
self.arm(ready_pat, break_x, span)
|
||||
};
|
||||
|
||||
// `::std::task::Poll::Pending => {}`
|
||||
let pending_arm = {
|
||||
let pending_pat = self.pat_lang_item_variant(span, hir::LangItem::PollPending, &[]);
|
||||
let empty_block = self.expr_block_empty(span);
|
||||
self.arm(pending_pat, empty_block)
|
||||
self.arm(pending_pat, empty_block, span)
|
||||
};
|
||||
|
||||
let inner_match_stmt = {
|
||||
|
|
@ -1027,7 +1027,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
});
|
||||
|
||||
// mut __awaitee => loop { ... }
|
||||
let awaitee_arm = self.arm(awaitee_pat, loop_expr);
|
||||
let awaitee_arm = self.arm(awaitee_pat, loop_expr, span);
|
||||
|
||||
// `match ::std::future::IntoFuture::into_future(<expr>) { ... }`
|
||||
let into_future_expr = match await_kind {
|
||||
|
|
@ -1817,7 +1817,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let break_expr =
|
||||
self.with_loop_scope(loop_hir_id, |this| this.expr_break_alloc(for_span));
|
||||
let pat = self.pat_none(for_span);
|
||||
self.arm(pat, break_expr)
|
||||
self.arm(pat, break_expr, for_span)
|
||||
};
|
||||
|
||||
// Some(<pat>) => <body>,
|
||||
|
|
@ -1826,7 +1826,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let body_block =
|
||||
self.with_loop_scope(loop_hir_id, |this| this.lower_block(body, false));
|
||||
let body_expr = self.arena.alloc(self.expr_block(body_block));
|
||||
self.arm(some_pat, body_expr)
|
||||
self.arm(some_pat, body_expr, for_span)
|
||||
};
|
||||
|
||||
// `mut iter`
|
||||
|
|
@ -1885,7 +1885,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let loop_expr = self.arena.alloc(hir::Expr { hir_id: loop_hir_id, kind, span: for_span });
|
||||
|
||||
// `mut iter => { ... }`
|
||||
let iter_arm = self.arm(iter_pat, loop_expr);
|
||||
let iter_arm = self.arm(iter_pat, loop_expr, for_span);
|
||||
|
||||
let match_expr = match loop_kind {
|
||||
ForLoopKind::For => {
|
||||
|
|
@ -1930,7 +1930,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::LangItem::IntoAsyncIterIntoIter,
|
||||
arena_vec![self; head],
|
||||
);
|
||||
let iter_arm = self.arm(async_iter_pat, inner_match_expr);
|
||||
let iter_arm = self.arm(async_iter_pat, inner_match_expr, for_span);
|
||||
self.arena.alloc(self.expr_match(
|
||||
for_span,
|
||||
iter,
|
||||
|
|
@ -1997,7 +1997,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let val_expr = self.expr_ident(span, val_ident, val_pat_nid);
|
||||
self.lower_attrs(val_expr.hir_id, &attrs, span, Target::Expression);
|
||||
let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
|
||||
self.arm(continue_pat, val_expr)
|
||||
self.arm(continue_pat, val_expr, try_span)
|
||||
};
|
||||
|
||||
// `ControlFlow::Break(residual) =>
|
||||
|
|
@ -2040,7 +2040,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.lower_attrs(ret_expr.hir_id, &attrs, span, Target::Expression);
|
||||
|
||||
let break_pat = self.pat_cf_break(try_span, residual_local);
|
||||
self.arm(break_pat, ret_expr)
|
||||
self.arm(break_pat, ret_expr, try_span)
|
||||
};
|
||||
|
||||
hir::ExprKind::Match(
|
||||
|
|
@ -2368,12 +2368,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
&mut self,
|
||||
pat: &'hir hir::Pat<'hir>,
|
||||
expr: &'hir hir::Expr<'hir>,
|
||||
span: Span,
|
||||
) -> hir::Arm<'hir> {
|
||||
hir::Arm {
|
||||
hir_id: self.next_id(),
|
||||
pat,
|
||||
guard: None,
|
||||
span: self.lower_span(expr.span),
|
||||
span: self.lower_span(span),
|
||||
body: expr,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ident,
|
||||
generics,
|
||||
ty,
|
||||
rhs,
|
||||
rhs_kind,
|
||||
define_opaque,
|
||||
}) => {
|
||||
let ident = self.lower_ident(*ident);
|
||||
|
|
@ -301,7 +301,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ty,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
|
||||
);
|
||||
let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), span);
|
||||
let rhs = this.lower_const_item_rhs(rhs_kind, span);
|
||||
(ty, rhs)
|
||||
},
|
||||
);
|
||||
|
|
@ -827,7 +827,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir_id,
|
||||
def_id: self.local_def_id(v.id),
|
||||
data: self.lower_variant_data(hir_id, item_kind, &v.data),
|
||||
disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)),
|
||||
disr_expr: v
|
||||
.disr_expr
|
||||
.as_ref()
|
||||
.map(|e| self.lower_anon_const_to_anon_const(e, e.value.span)),
|
||||
ident: self.lower_ident(v.ident),
|
||||
span: self.lower_span(v.span),
|
||||
}
|
||||
|
|
@ -917,7 +920,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
|
||||
},
|
||||
vis_span: self.lower_span(f.vis.span),
|
||||
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
|
||||
default: f
|
||||
.default
|
||||
.as_ref()
|
||||
.map(|v| self.lower_anon_const_to_anon_const(v, v.value.span)),
|
||||
ty,
|
||||
safety: self.lower_safety(f.safety, hir::Safety::Safe),
|
||||
}
|
||||
|
|
@ -935,7 +941,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
let (ident, generics, kind, has_default) = match &i.kind {
|
||||
AssocItemKind::Const(box ConstItem {
|
||||
ident, generics, ty, rhs, define_opaque, ..
|
||||
ident,
|
||||
generics,
|
||||
ty,
|
||||
rhs_kind,
|
||||
define_opaque,
|
||||
..
|
||||
}) => {
|
||||
let (generics, kind) = self.lower_generics(
|
||||
generics,
|
||||
|
|
@ -946,15 +957,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ty,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
|
||||
);
|
||||
let rhs = rhs
|
||||
.as_ref()
|
||||
.map(|rhs| this.lower_const_item_rhs(attrs, Some(rhs), i.span));
|
||||
hir::TraitItemKind::Const(ty, rhs)
|
||||
// Trait associated consts don't need an expression/body.
|
||||
let rhs = if rhs_kind.has_expr() {
|
||||
Some(this.lower_const_item_rhs(rhs_kind, i.span))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
hir::TraitItemKind::Const(ty, rhs, rhs_kind.is_type_const().into())
|
||||
},
|
||||
);
|
||||
|
||||
if define_opaque.is_some() {
|
||||
if rhs.is_some() {
|
||||
if rhs_kind.has_expr() {
|
||||
self.lower_define_opaque(hir_id, &define_opaque);
|
||||
} else {
|
||||
self.dcx().span_err(
|
||||
|
|
@ -964,7 +978,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
(*ident, generics, kind, rhs.is_some())
|
||||
(*ident, generics, kind, rhs_kind.has_expr())
|
||||
}
|
||||
AssocItemKind::Fn(box Fn {
|
||||
sig, ident, generics, body: None, define_opaque, ..
|
||||
|
|
@ -1148,7 +1162,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
let (ident, (generics, kind)) = match &i.kind {
|
||||
AssocItemKind::Const(box ConstItem {
|
||||
ident, generics, ty, rhs, define_opaque, ..
|
||||
ident,
|
||||
generics,
|
||||
ty,
|
||||
rhs_kind,
|
||||
define_opaque,
|
||||
..
|
||||
}) => (
|
||||
*ident,
|
||||
self.lower_generics(
|
||||
|
|
@ -1161,7 +1180,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
|
||||
);
|
||||
this.lower_define_opaque(hir_id, &define_opaque);
|
||||
let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), i.span);
|
||||
let rhs = this.lower_const_item_rhs(rhs_kind, i.span);
|
||||
hir::ImplItemKind::Const(ty, rhs)
|
||||
},
|
||||
),
|
||||
|
|
@ -1391,7 +1410,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// create a fake body so that the entire rest of the compiler doesn't have to deal with
|
||||
// this as a special case.
|
||||
return self.lower_fn_body(decl, contract, |this| {
|
||||
if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic))
|
||||
if find_attr!(attrs, AttributeKind::RustcIntrinsic)
|
||||
|| this.tcx.is_sdylib_interface_build()
|
||||
{
|
||||
let span = this.lower_span(span);
|
||||
|
|
|
|||
|
|
@ -2374,15 +2374,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
fn lower_const_item_rhs(
|
||||
&mut self,
|
||||
attrs: &[hir::Attribute],
|
||||
rhs: Option<&ConstItemRhs>,
|
||||
rhs_kind: &ConstItemRhsKind,
|
||||
span: Span,
|
||||
) -> hir::ConstItemRhs<'hir> {
|
||||
match rhs {
|
||||
Some(ConstItemRhs::TypeConst(anon)) => {
|
||||
match rhs_kind {
|
||||
ConstItemRhsKind::Body { rhs: Some(body) } => {
|
||||
hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body)))
|
||||
}
|
||||
ConstItemRhsKind::Body { rhs: None } => {
|
||||
hir::ConstItemRhs::Body(self.lower_const_body(span, None))
|
||||
}
|
||||
ConstItemRhsKind::TypeConst { rhs: Some(anon) } => {
|
||||
hir::ConstItemRhs::TypeConst(self.lower_anon_const_to_const_arg_and_alloc(anon))
|
||||
}
|
||||
None if find_attr!(attrs, AttributeKind::TypeConst(_)) => {
|
||||
ConstItemRhsKind::TypeConst { rhs: None } => {
|
||||
let const_arg = ConstArg {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ConstArgKind::Error(
|
||||
|
|
@ -2392,10 +2397,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
hir::ConstItemRhs::TypeConst(self.arena.alloc(const_arg))
|
||||
}
|
||||
Some(ConstItemRhs::Body(body)) => {
|
||||
hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body)))
|
||||
}
|
||||
None => hir::ConstItemRhs::Body(self.lower_const_body(span, None)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2425,15 +2426,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
);
|
||||
|
||||
let lowered_args = self.arena.alloc_from_iter(args.iter().map(|arg| {
|
||||
let const_arg = if let ExprKind::ConstBlock(anon_const) = &arg.kind {
|
||||
let def_id = self.local_def_id(anon_const.id);
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
assert_eq!(DefKind::AnonConst, def_kind);
|
||||
self.lower_anon_const_to_const_arg(anon_const)
|
||||
} else {
|
||||
self.lower_expr_to_const_arg_direct(arg)
|
||||
};
|
||||
|
||||
let const_arg = self.lower_expr_to_const_arg_direct(arg);
|
||||
&*self.arena.alloc(const_arg)
|
||||
}));
|
||||
|
||||
|
|
@ -2445,16 +2438,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
ExprKind::Tup(exprs) => {
|
||||
let exprs = self.arena.alloc_from_iter(exprs.iter().map(|expr| {
|
||||
let expr = if let ExprKind::ConstBlock(anon_const) = &expr.kind {
|
||||
let def_id = self.local_def_id(anon_const.id);
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
assert_eq!(DefKind::AnonConst, def_kind);
|
||||
|
||||
self.lower_anon_const_to_const_arg(anon_const)
|
||||
} else {
|
||||
self.lower_expr_to_const_arg_direct(&expr)
|
||||
};
|
||||
|
||||
let expr = self.lower_expr_to_const_arg_direct(&expr);
|
||||
&*self.arena.alloc(expr)
|
||||
}));
|
||||
|
||||
|
|
@ -2494,16 +2478,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// then go unused as the `Target::ExprField` is not actually
|
||||
// corresponding to `Node::ExprField`.
|
||||
self.lower_attrs(hir_id, &f.attrs, f.span, Target::ExprField);
|
||||
|
||||
let expr = if let ExprKind::ConstBlock(anon_const) = &f.expr.kind {
|
||||
let def_id = self.local_def_id(anon_const.id);
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
assert_eq!(DefKind::AnonConst, def_kind);
|
||||
|
||||
self.lower_anon_const_to_const_arg(anon_const)
|
||||
} else {
|
||||
self.lower_expr_to_const_arg_direct(&f.expr)
|
||||
};
|
||||
let expr = self.lower_expr_to_const_arg_direct(&f.expr);
|
||||
|
||||
&*self.arena.alloc(hir::ConstArgExprField {
|
||||
hir_id,
|
||||
|
|
@ -2521,13 +2496,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
ExprKind::Array(elements) => {
|
||||
let lowered_elems = self.arena.alloc_from_iter(elements.iter().map(|element| {
|
||||
let const_arg = if let ExprKind::ConstBlock(anon_const) = &element.kind {
|
||||
let def_id = self.local_def_id(anon_const.id);
|
||||
assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id));
|
||||
self.lower_anon_const_to_const_arg(anon_const)
|
||||
} else {
|
||||
self.lower_expr_to_const_arg_direct(element)
|
||||
};
|
||||
let const_arg = self.lower_expr_to_const_arg_direct(element);
|
||||
&*self.arena.alloc(const_arg)
|
||||
}));
|
||||
let array_expr = self.arena.alloc(hir::ConstArgArrayExpr {
|
||||
|
|
@ -2557,6 +2526,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
| ExprKind::Call(..)
|
||||
| ExprKind::Tup(..)
|
||||
| ExprKind::Array(..)
|
||||
| ExprKind::ConstBlock(..)
|
||||
)
|
||||
{
|
||||
return self.lower_expr_to_const_arg_direct(expr);
|
||||
|
|
@ -2570,10 +2540,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
ConstArg {
|
||||
hir_id: self.lower_node_id(expr.id),
|
||||
kind: hir::ConstArgKind::Literal(literal.node),
|
||||
kind: hir::ConstArgKind::Literal { lit: literal.node, negated: false },
|
||||
span,
|
||||
}
|
||||
}
|
||||
ExprKind::Unary(UnOp::Neg, inner_expr)
|
||||
if let ExprKind::Lit(literal) = &inner_expr.kind =>
|
||||
{
|
||||
let span = expr.span;
|
||||
let literal = self.lower_lit(literal, span);
|
||||
|
||||
ConstArg {
|
||||
hir_id: self.lower_node_id(expr.id),
|
||||
kind: hir::ConstArgKind::Literal { lit: literal.node, negated: true },
|
||||
span,
|
||||
}
|
||||
}
|
||||
ExprKind::ConstBlock(anon_const) => {
|
||||
let def_id = self.local_def_id(anon_const.id);
|
||||
assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id));
|
||||
self.lower_anon_const_to_const_arg(anon_const, span)
|
||||
}
|
||||
_ => overly_complex_const(self),
|
||||
}
|
||||
}
|
||||
|
|
@ -2584,11 +2571,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
&mut self,
|
||||
anon: &AnonConst,
|
||||
) -> &'hir hir::ConstArg<'hir> {
|
||||
self.arena.alloc(self.lower_anon_const_to_const_arg(anon))
|
||||
self.arena.alloc(self.lower_anon_const_to_const_arg(anon, anon.value.span))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> {
|
||||
fn lower_anon_const_to_const_arg(
|
||||
&mut self,
|
||||
anon: &AnonConst,
|
||||
span: Span,
|
||||
) -> hir::ConstArg<'hir> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
// We cannot change parsing depending on feature gates available,
|
||||
|
|
@ -2599,7 +2590,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
if tcx.features().min_generic_const_args() {
|
||||
return match anon.mgca_disambiguation {
|
||||
MgcaDisambiguation::AnonConst => {
|
||||
let lowered_anon = self.lower_anon_const_to_anon_const(anon);
|
||||
let lowered_anon = self.lower_anon_const_to_anon_const(anon, span);
|
||||
ConstArg {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ConstArgKind::Anon(lowered_anon),
|
||||
|
|
@ -2645,7 +2636,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
}
|
||||
|
||||
let lowered_anon = self.lower_anon_const_to_anon_const(anon);
|
||||
let lowered_anon = self.lower_anon_const_to_anon_const(anon, anon.value.span);
|
||||
ConstArg {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ConstArgKind::Anon(lowered_anon),
|
||||
|
|
@ -2655,7 +2646,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
/// See [`hir::ConstArg`] for when to use this function vs
|
||||
/// [`Self::lower_anon_const_to_const_arg`].
|
||||
fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst {
|
||||
fn lower_anon_const_to_anon_const(
|
||||
&mut self,
|
||||
c: &AnonConst,
|
||||
span: Span,
|
||||
) -> &'hir hir::AnonConst {
|
||||
self.arena.alloc(self.with_new_scopes(c.value.span, |this| {
|
||||
let def_id = this.local_def_id(c.id);
|
||||
let hir_id = this.lower_node_id(c.id);
|
||||
|
|
@ -2663,7 +2658,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
def_id,
|
||||
hir_id,
|
||||
body: this.lower_const_body(c.value.span, Some(&c.value)),
|
||||
span: this.lower_span(c.value.span),
|
||||
span: this.lower_span(span),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1361,9 +1361,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
}
|
||||
});
|
||||
}
|
||||
ItemKind::Const(box ConstItem { defaultness, ident, rhs, .. }) => {
|
||||
ItemKind::Const(box ConstItem { defaultness, ident, rhs_kind, .. }) => {
|
||||
self.check_defaultness(item.span, *defaultness);
|
||||
if rhs.is_none() {
|
||||
if !rhs_kind.has_expr() {
|
||||
self.dcx().emit_err(errors::ConstWithoutBody {
|
||||
span: item.span,
|
||||
replace_span: self.ending_semi_or_hi(item.span),
|
||||
|
|
@ -1715,11 +1715,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
|
||||
if let AssocCtxt::Impl { .. } = ctxt {
|
||||
match &item.kind {
|
||||
AssocItemKind::Const(box ConstItem { rhs: None, .. }) => {
|
||||
self.dcx().emit_err(errors::AssocConstWithoutBody {
|
||||
span: item.span,
|
||||
replace_span: self.ending_semi_or_hi(item.span),
|
||||
});
|
||||
AssocItemKind::Const(box ConstItem { rhs_kind, .. }) => {
|
||||
if !rhs_kind.has_expr() {
|
||||
self.dcx().emit_err(errors::AssocConstWithoutBody {
|
||||
span: item.span,
|
||||
replace_span: self.ending_semi_or_hi(item.span),
|
||||
});
|
||||
}
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { body, .. }) => {
|
||||
if body.is_none() && !self.is_sdylib_interface {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::ParamKindOrd;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic, inline_fluent};
|
||||
use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_span::{Ident, Span, Symbol};
|
||||
|
||||
|
|
@ -503,7 +503,7 @@ pub(crate) struct AutoTraitItems {
|
|||
#[primary_span]
|
||||
pub spans: Vec<Span>,
|
||||
#[suggestion(
|
||||
"remove the super traits or lifetime bounds",
|
||||
"remove the associated items",
|
||||
code = "",
|
||||
applicability = "machine-applicable",
|
||||
style = "tool-only"
|
||||
|
|
@ -927,19 +927,15 @@ pub(crate) struct FeatureOnNonNightly {
|
|||
pub sugg: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[help(
|
||||
"the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable"
|
||||
)]
|
||||
pub(crate) struct StableFeature {
|
||||
pub name: Symbol,
|
||||
pub since: Symbol,
|
||||
}
|
||||
|
||||
impl Subdiagnostic for StableFeature {
|
||||
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
|
||||
diag.arg("name", self.name);
|
||||
diag.arg("since", self.since);
|
||||
diag.help(inline_fluent!("the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable"));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed")]
|
||||
#[help("remove one of these features")]
|
||||
|
|
@ -950,6 +946,16 @@ pub(crate) struct IncompatibleFeatures {
|
|||
pub f2: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`{$parent}` requires {$missing} to be enabled")]
|
||||
#[help("enable all of these features")]
|
||||
pub(crate) struct MissingDependentFeatures {
|
||||
#[primary_span]
|
||||
pub parent_span: Span,
|
||||
pub parent: Symbol,
|
||||
pub missing: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("negative bounds are not supported")]
|
||||
pub(crate) struct NegativeBoundUnsupported {
|
||||
|
|
|
|||
|
|
@ -249,6 +249,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => {
|
||||
self.check_impl_trait(ty, false)
|
||||
}
|
||||
ast::ItemKind::Const(box ast::ConstItem {
|
||||
rhs_kind: ast::ConstItemRhsKind::TypeConst { .. },
|
||||
..
|
||||
}) => {
|
||||
// Make sure this is only allowed if the feature gate is enabled.
|
||||
// #![feature(min_generic_const_args)]
|
||||
gate!(&self, min_generic_const_args, i.span, "top-level `type const` are unstable");
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -330,15 +338,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
fn visit_expr(&mut self, e: &'a ast::Expr) {
|
||||
match e.kind {
|
||||
ast::ExprKind::TryBlock(_, None) => {
|
||||
// `try { ... }` is old and is only gated post-expansion here.
|
||||
gate!(&self, try_blocks, e.span, "`try` expression is experimental");
|
||||
}
|
||||
ast::ExprKind::TryBlock(_, Some(_)) => {
|
||||
gate!(
|
||||
&self,
|
||||
try_blocks_heterogeneous,
|
||||
e.span,
|
||||
"`try bikeshed` expression is experimental"
|
||||
);
|
||||
// `try_blocks_heterogeneous` is new, and gated pre-expansion instead.
|
||||
}
|
||||
ast::ExprKind::Lit(token::Lit {
|
||||
kind: token::LitKind::Float | token::LitKind::Integer,
|
||||
|
|
@ -422,6 +426,20 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
}
|
||||
false
|
||||
}
|
||||
ast::AssocItemKind::Const(box ast::ConstItem {
|
||||
rhs_kind: ast::ConstItemRhsKind::TypeConst { .. },
|
||||
..
|
||||
}) => {
|
||||
// Make sure this is only allowed if the feature gate is enabled.
|
||||
// #![feature(min_generic_const_args)]
|
||||
gate!(
|
||||
&self,
|
||||
min_generic_const_args,
|
||||
i.span,
|
||||
"associated `type const` are unstable"
|
||||
);
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if let ast::Defaultness::Default(_) = i.kind.defaultness() {
|
||||
|
|
@ -441,6 +459,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
||||
maybe_stage_features(sess, features, krate);
|
||||
check_incompatible_features(sess, features);
|
||||
check_dependent_features(sess, features);
|
||||
check_new_solver_banned_features(sess, features);
|
||||
|
||||
let mut visitor = PostExpansionVisitor { sess, features };
|
||||
|
|
@ -499,6 +518,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
half_open_range_patterns_in_slices,
|
||||
"half-open range patterns in slices are unstable"
|
||||
);
|
||||
gate_all!(try_blocks_heterogeneous, "`try bikeshed` expression is experimental");
|
||||
gate_all!(yeet_expr, "`do yeet` expression is experimental");
|
||||
gate_all!(const_closures, "const closures are experimental");
|
||||
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
|
||||
|
|
@ -527,6 +547,27 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// `mgca_type_const_syntax` is part of `min_generic_const_args` so either
|
||||
// or both are enabled we don't need to emit a feature error.
|
||||
if let Some(spans) = spans.get(&sym::mgca_type_const_syntax) {
|
||||
for span in spans {
|
||||
if visitor.features.min_generic_const_args()
|
||||
|| visitor.features.mgca_type_const_syntax()
|
||||
|| span.allows_unstable(sym::min_generic_const_args)
|
||||
|| span.allows_unstable(sym::mgca_type_const_syntax)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
feature_err(
|
||||
&visitor.sess,
|
||||
sym::min_generic_const_args,
|
||||
*span,
|
||||
"`type const` syntax is experimental",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
gate_all!(global_registration, "global registration is experimental");
|
||||
gate_all!(return_type_notation, "return type notation is experimental");
|
||||
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
|
||||
|
|
@ -649,6 +690,27 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_dependent_features(sess: &Session, features: &Features) {
|
||||
for &(parent, children) in
|
||||
rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent))
|
||||
{
|
||||
if children.iter().any(|f| !features.enabled(*f)) {
|
||||
let parent_span = features
|
||||
.enabled_features_iter_stable_order()
|
||||
.find_map(|(name, span)| (name == parent).then_some(span))
|
||||
.unwrap();
|
||||
// FIXME: should probably format this in fluent instead of here
|
||||
let missing = children
|
||||
.iter()
|
||||
.filter(|f| !features.enabled(**f))
|
||||
.map(|s| format!("`{}`", s.as_str()))
|
||||
.intersperse(String::from(", "))
|
||||
.collect();
|
||||
sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_new_solver_banned_features(sess: &Session, features: &Features) {
|
||||
if !sess.opts.unstable_opts.next_solver.globally {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
// tidy-alphabetical-start
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(iter_is_partitioned)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ impl<'a> State<'a> {
|
|||
ident,
|
||||
generics,
|
||||
ty,
|
||||
rhs,
|
||||
rhs_kind,
|
||||
define_opaque,
|
||||
}) => {
|
||||
self.print_item_const(
|
||||
|
|
@ -229,7 +229,7 @@ impl<'a> State<'a> {
|
|||
None,
|
||||
generics,
|
||||
ty,
|
||||
rhs.as_ref().map(|ct| ct.expr()),
|
||||
rhs_kind.expr(),
|
||||
&item.vis,
|
||||
ast::Safety::Default,
|
||||
*defaultness,
|
||||
|
|
@ -573,7 +573,7 @@ impl<'a> State<'a> {
|
|||
ident,
|
||||
generics,
|
||||
ty,
|
||||
rhs,
|
||||
rhs_kind,
|
||||
define_opaque,
|
||||
}) => {
|
||||
self.print_item_const(
|
||||
|
|
@ -581,7 +581,7 @@ impl<'a> State<'a> {
|
|||
None,
|
||||
generics,
|
||||
ty,
|
||||
rhs.as_ref().map(|ct| ct.expr()),
|
||||
rhs_kind.expr(),
|
||||
vis,
|
||||
ast::Safety::Default,
|
||||
*defaultness,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ edition = "2024"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
|
|||
|
||||
pub(crate) struct UnstableFeatureBoundParser;
|
||||
impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
|
||||
const PATH: &[rustc_span::Symbol] = &[sym::unstable_feature_bound];
|
||||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
|
|
|
|||
|
|
@ -1,22 +1,35 @@
|
|||
use rustc_ast::token::Token;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{AttrStyle, NodeId, token};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_feature::{AttributeTemplate, Features};
|
||||
use rustc_hir::attrs::CfgEntry;
|
||||
use rustc_hir::{AttrPath, Target};
|
||||
use rustc_parse::exp;
|
||||
use rustc_parse::parser::{Parser, Recovery};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{ErrorGuaranteed, Span, sym};
|
||||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES;
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
|
||||
|
||||
use crate::parser::MetaItemOrLitParser;
|
||||
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum CfgSelectPredicate {
|
||||
Cfg(CfgEntry),
|
||||
Wildcard(Token),
|
||||
}
|
||||
|
||||
impl CfgSelectPredicate {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(),
|
||||
CfgSelectPredicate::Wildcard(token) => token.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CfgSelectBranches {
|
||||
/// All the conditional branches.
|
||||
|
|
@ -115,5 +128,102 @@ pub fn parse_cfg_select(
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(features) = features
|
||||
&& features.enabled(sym::cfg_select)
|
||||
{
|
||||
let it = branches
|
||||
.reachable
|
||||
.iter()
|
||||
.map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone()))
|
||||
.chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t)))
|
||||
.chain(
|
||||
branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)),
|
||||
);
|
||||
|
||||
lint_unreachable(p, it, lint_node_id);
|
||||
}
|
||||
|
||||
Ok(branches)
|
||||
}
|
||||
|
||||
fn lint_unreachable(
|
||||
p: &mut Parser<'_>,
|
||||
predicates: impl Iterator<Item = CfgSelectPredicate>,
|
||||
lint_node_id: NodeId,
|
||||
) {
|
||||
// Symbols that have a known value.
|
||||
let mut known = FxHashMap::<Symbol, bool>::default();
|
||||
let mut wildcard_span = None;
|
||||
let mut it = predicates;
|
||||
|
||||
let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| {
|
||||
let span = predicate.span();
|
||||
p.psess.buffer_lint(
|
||||
UNREACHABLE_CFG_SELECT_PREDICATES,
|
||||
span,
|
||||
lint_node_id,
|
||||
BuiltinLintDiag::UnreachableCfg { span, wildcard_span },
|
||||
);
|
||||
};
|
||||
|
||||
for predicate in &mut it {
|
||||
let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else {
|
||||
wildcard_span = Some(predicate.span());
|
||||
break;
|
||||
};
|
||||
|
||||
match cfg_entry {
|
||||
CfgEntry::Bool(true, _) => {
|
||||
wildcard_span = Some(predicate.span());
|
||||
break;
|
||||
}
|
||||
CfgEntry::Bool(false, _) => continue,
|
||||
CfgEntry::NameValue { name, value, .. } => match value {
|
||||
None => {
|
||||
// `name` will be false in all subsequent branches.
|
||||
let current = known.insert(*name, false);
|
||||
|
||||
match current {
|
||||
None => continue,
|
||||
Some(false) => {
|
||||
branch_is_unreachable(predicate, None);
|
||||
break;
|
||||
}
|
||||
Some(true) => {
|
||||
// this branch will be taken, so all subsequent branches are unreachable.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(_) => { /* for now we don't bother solving these */ }
|
||||
},
|
||||
CfgEntry::Not(inner, _) => match &**inner {
|
||||
CfgEntry::NameValue { name, value: None, .. } => {
|
||||
// `name` will be true in all subsequent branches.
|
||||
let current = known.insert(*name, true);
|
||||
|
||||
match current {
|
||||
None => continue,
|
||||
Some(true) => {
|
||||
branch_is_unreachable(predicate, None);
|
||||
break;
|
||||
}
|
||||
Some(false) => {
|
||||
// this branch will be taken, so all subsequent branches are unreachable.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { /* for now we don't bother solving these */ }
|
||||
},
|
||||
CfgEntry::All(_, _) | CfgEntry::Any(_, _) => {
|
||||
/* for now we don't bother solving these */
|
||||
}
|
||||
CfgEntry::Version(..) => { /* don't bother solving these */ }
|
||||
}
|
||||
}
|
||||
|
||||
for predicate in it {
|
||||
branch_is_unreachable(predicate, wildcard_span)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -283,3 +283,12 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcPreserveUbChecksParser {
|
|||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcNoImplicitBoundsParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcNoImplicitBoundsParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_no_implicit_bounds];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_hir::attrs::{DeprecatedSince, Deprecation};
|
||||
use rustc_hir::{RustcVersion, VERSION_PLACEHOLDER};
|
||||
|
||||
use super::prelude::*;
|
||||
use super::util::parse_version;
|
||||
|
|
@ -143,6 +144,8 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
|||
DeprecatedSince::Future
|
||||
} else if !is_rustc {
|
||||
DeprecatedSince::NonStandard(since)
|
||||
} else if since.as_str() == VERSION_PLACEHOLDER {
|
||||
DeprecatedSince::RustcVersion(RustcVersion::CURRENT)
|
||||
} else if let Some(version) = parse_version(since) {
|
||||
DeprecatedSince::RustcVersion(version)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use super::prelude::*;
|
|||
pub(crate) struct InlineParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
||||
const PATH: &'static [Symbol] = &[sym::inline];
|
||||
const PATH: &[Symbol] = &[sym::inline];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
|
|
@ -67,7 +67,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
|||
pub(crate) struct RustcForceInlineParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_force_inline];
|
||||
const PATH: &[Symbol] = &[sym::rustc_force_inline];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_session::lint::builtin::AMBIGUOUS_DERIVE_HELPERS;
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
const PROC_MACRO_ALLOWED_TARGETS: AllowedTargets =
|
||||
|
|
@ -126,6 +129,13 @@ fn parse_derive_like<S: Stage>(
|
|||
cx.expected_identifier(ident.span);
|
||||
return None;
|
||||
}
|
||||
if rustc_feature::is_builtin_attr_name(ident.name) {
|
||||
cx.emit_lint(
|
||||
AMBIGUOUS_DERIVE_HELPERS,
|
||||
AttributeLintKind::AmbiguousDeriveHelpers,
|
||||
ident.span,
|
||||
);
|
||||
}
|
||||
attributes.push(ident.name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
|
|||
pub(crate) struct AlignParser(Option<(Align, Span)>);
|
||||
|
||||
impl AlignParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_align];
|
||||
const PATH: &[Symbol] = &[sym::rustc_align];
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
|
||||
|
||||
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
|
||||
|
|
@ -329,7 +329,7 @@ impl<S: Stage> AttributeParser<S> for AlignParser {
|
|||
pub(crate) struct AlignStaticParser(AlignParser);
|
||||
|
||||
impl AlignStaticParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_align_static];
|
||||
const PATH: &[Symbol] = &[sym::rustc_align_static];
|
||||
const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
|
||||
|
||||
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use std::path::PathBuf;
|
|||
|
||||
use rustc_ast::{LitIntType, LitKind, MetaItemLit};
|
||||
use rustc_hir::attrs::{
|
||||
BorrowckGraphvizFormatKind, RustcCleanAttribute, RustcCleanQueries, RustcLayoutType,
|
||||
BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior,
|
||||
DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcLayoutType,
|
||||
RustcMirKind,
|
||||
};
|
||||
use rustc_session::errors;
|
||||
|
|
@ -10,12 +11,14 @@ use rustc_span::Symbol;
|
|||
|
||||
use super::prelude::*;
|
||||
use super::util::parse_single_integer;
|
||||
use crate::session_diagnostics::{AttributeRequiresOpt, RustcScalableVectorCountOutOfRange};
|
||||
use crate::session_diagnostics::{
|
||||
AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange,
|
||||
};
|
||||
|
||||
pub(crate) struct RustcMainParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcMainParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_main];
|
||||
const PATH: &[Symbol] = &[sym::rustc_main];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcMain;
|
||||
|
|
@ -100,7 +103,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcNoImplicitAutorefsParser {
|
|||
pub(crate) struct RustcLayoutScalarValidRangeStartParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStartParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start];
|
||||
const PATH: &[Symbol] = &[sym::rustc_layout_scalar_valid_range_start];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
|
|
@ -115,7 +118,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStartPars
|
|||
pub(crate) struct RustcLayoutScalarValidRangeEndParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEndParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end];
|
||||
const PATH: &[Symbol] = &[sym::rustc_layout_scalar_valid_range_end];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
|
|
@ -204,6 +207,325 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcLintOptTyParser {
|
|||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintOptTy;
|
||||
}
|
||||
|
||||
fn parse_cgu_fields<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
accepts_kind: bool,
|
||||
) -> Option<(Symbol, Symbol, Option<CguKind>)> {
|
||||
let Some(args) = args.list() else {
|
||||
cx.expected_list(cx.attr_span, args);
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut cfg = None::<(Symbol, Span)>;
|
||||
let mut module = None::<(Symbol, Span)>;
|
||||
let mut kind = None::<(Symbol, Span)>;
|
||||
|
||||
for arg in args.mixed() {
|
||||
let Some(arg) = arg.meta_item() else {
|
||||
cx.expected_name_value(args.span, None);
|
||||
continue;
|
||||
};
|
||||
|
||||
let res = match arg.ident().map(|i| i.name) {
|
||||
Some(sym::cfg) => &mut cfg,
|
||||
Some(sym::module) => &mut module,
|
||||
Some(sym::kind) if accepts_kind => &mut kind,
|
||||
_ => {
|
||||
cx.expected_specific_argument(
|
||||
arg.path().span(),
|
||||
if accepts_kind {
|
||||
&[sym::cfg, sym::module, sym::kind]
|
||||
} else {
|
||||
&[sym::cfg, sym::module]
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(i) = arg.args().name_value() else {
|
||||
cx.expected_name_value(arg.span(), None);
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(str) = i.value_as_str() else {
|
||||
cx.expected_string_literal(i.value_span, Some(i.value_as_lit()));
|
||||
continue;
|
||||
};
|
||||
|
||||
if res.is_some() {
|
||||
cx.duplicate_key(arg.span(), arg.ident().unwrap().name);
|
||||
continue;
|
||||
}
|
||||
|
||||
*res = Some((str, i.value_span));
|
||||
}
|
||||
|
||||
let Some((cfg, _)) = cfg else {
|
||||
cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg });
|
||||
return None;
|
||||
};
|
||||
let Some((module, _)) = module else {
|
||||
cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module });
|
||||
return None;
|
||||
};
|
||||
let kind = if let Some((kind, span)) = kind {
|
||||
Some(match kind {
|
||||
sym::no => CguKind::No,
|
||||
sym::pre_dash_lto => CguKind::PreDashLto,
|
||||
sym::post_dash_lto => CguKind::PostDashLto,
|
||||
sym::any => CguKind::Any,
|
||||
_ => {
|
||||
cx.expected_specific_argument_strings(
|
||||
span,
|
||||
&[sym::no, sym::pre_dash_lto, sym::post_dash_lto, sym::any],
|
||||
);
|
||||
return None;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// return None so that an unwrap for the attributes that need it is ok.
|
||||
if accepts_kind {
|
||||
cx.emit_err(CguFieldsMissing {
|
||||
span: args.span,
|
||||
name: &cx.attr_path,
|
||||
field: sym::kind,
|
||||
});
|
||||
return None;
|
||||
};
|
||||
|
||||
None
|
||||
};
|
||||
|
||||
Some((cfg, module, kind))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct RustcCguTestAttributeParser {
|
||||
items: ThinVec<(Span, CguFields)>,
|
||||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for RustcCguTestAttributeParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[
|
||||
(
|
||||
&[sym::rustc_partition_reused],
|
||||
template!(List: &[r#"cfg = "...", module = "...""#]),
|
||||
|this, cx, args| {
|
||||
this.items.extend(parse_cgu_fields(cx, args, false).map(|(cfg, module, _)| {
|
||||
(cx.attr_span, CguFields::PartitionReused { cfg, module })
|
||||
}));
|
||||
},
|
||||
),
|
||||
(
|
||||
&[sym::rustc_partition_codegened],
|
||||
template!(List: &[r#"cfg = "...", module = "...""#]),
|
||||
|this, cx, args| {
|
||||
this.items.extend(parse_cgu_fields(cx, args, false).map(|(cfg, module, _)| {
|
||||
(cx.attr_span, CguFields::PartitionCodegened { cfg, module })
|
||||
}));
|
||||
},
|
||||
),
|
||||
(
|
||||
&[sym::rustc_expected_cgu_reuse],
|
||||
template!(List: &[r#"cfg = "...", module = "...", kind = "...""#]),
|
||||
|this, cx, args| {
|
||||
this.items.extend(parse_cgu_fields(cx, args, true).map(|(cfg, module, kind)| {
|
||||
// unwrap ok because if not given, we return None in `parse_cgu_fields`.
|
||||
(cx.attr_span, CguFields::ExpectedCguReuse { cfg, module, kind: kind.unwrap() })
|
||||
}));
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]);
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::RustcCguTestAttr(self.items))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcDeprecatedSafe2024Parser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcDeprecatedSafe2024Parser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_deprecated_safe_2024];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &[r#"audit_that = "...""#]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(args) = args.list() else {
|
||||
cx.expected_list(cx.attr_span, args);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(single) = args.single() else {
|
||||
cx.expected_single_argument(args.span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(arg) = single.meta_item() else {
|
||||
cx.expected_name_value(args.span, None);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(args) = arg.word_is(sym::audit_that) else {
|
||||
cx.expected_specific_argument(arg.span(), &[sym::audit_that]);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(arg.span(), Some(sym::audit_that));
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(suggestion) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::RustcDeprecatedSafe2024 { suggestion })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcConversionSuggestionParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcConversionSuggestionParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_conversion_suggestion];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConversionSuggestion;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcCaptureAnalysisParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcCaptureAnalysisParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_capture_analysis];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Closure)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCaptureAnalysis;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcNeverTypeOptionsParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcNeverTypeOptionsParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_never_type_options];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &[
|
||||
r#"fallback = "unit", "never", "no""#,
|
||||
r#"diverging_block_default = "unit", "never""#,
|
||||
]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span, args);
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut fallback = None::<Ident>;
|
||||
let mut diverging_block_default = None::<Ident>;
|
||||
|
||||
for arg in list.mixed() {
|
||||
let Some(meta) = arg.meta_item() else {
|
||||
cx.expected_name_value(arg.span(), None);
|
||||
continue;
|
||||
};
|
||||
|
||||
let res = match meta.ident().map(|i| i.name) {
|
||||
Some(sym::fallback) => &mut fallback,
|
||||
Some(sym::diverging_block_default) => &mut diverging_block_default,
|
||||
_ => {
|
||||
cx.expected_specific_argument(
|
||||
meta.path().span(),
|
||||
&[sym::fallback, sym::diverging_block_default],
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(nv) = meta.args().name_value() else {
|
||||
cx.expected_name_value(meta.span(), None);
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(field) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
continue;
|
||||
};
|
||||
|
||||
if res.is_some() {
|
||||
cx.duplicate_key(meta.span(), meta.ident().unwrap().name);
|
||||
continue;
|
||||
}
|
||||
|
||||
*res = Some(Ident { name: field, span: nv.value_span });
|
||||
}
|
||||
|
||||
let fallback = match fallback {
|
||||
None => None,
|
||||
Some(Ident { name: sym::unit, .. }) => Some(DivergingFallbackBehavior::ToUnit),
|
||||
Some(Ident { name: sym::never, .. }) => Some(DivergingFallbackBehavior::ToNever),
|
||||
Some(Ident { name: sym::no, .. }) => Some(DivergingFallbackBehavior::NoFallback),
|
||||
Some(Ident { span, .. }) => {
|
||||
cx.expected_specific_argument_strings(span, &[sym::unit, sym::never, sym::no]);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let diverging_block_default = match diverging_block_default {
|
||||
None => None,
|
||||
Some(Ident { name: sym::unit, .. }) => Some(DivergingBlockBehavior::Unit),
|
||||
Some(Ident { name: sym::never, .. }) => Some(DivergingBlockBehavior::Never),
|
||||
Some(Ident { span, .. }) => {
|
||||
cx.expected_specific_argument_strings(span, &[sym::unit, sym::no]);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(AttributeKind::RustcNeverTypeOptions { fallback, diverging_block_default })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcTrivialFieldReadsParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcTrivialFieldReadsParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcNoMirInlineParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcNoMirInlineParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_no_mir_inline];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoMirInline;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcLintQueryInstabilityParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcLintQueryInstabilityParser {
|
||||
|
|
@ -219,6 +541,22 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcLintQueryInstabilityParser {
|
|||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintQueryInstability;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcRegionsParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcRegionsParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_regions];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcRegions;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcLintUntrackedQueryInformationParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcLintUntrackedQueryInformationParser {
|
||||
|
|
@ -237,21 +575,11 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcLintUntrackedQueryInformationPa
|
|||
|
||||
pub(crate) struct RustcObjectLifetimeDefaultParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcObjectLifetimeDefaultParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_object_lifetime_default];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AttributeKind::RustcObjectLifetimeDefault)
|
||||
}
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcObjectLifetimeDefault;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcSimdMonomorphizeLaneLimitParser;
|
||||
|
|
@ -490,10 +818,11 @@ impl<S: Stage> CombineAttributeParser<S> for RustcMirParser {
|
|||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcNonConstTraitMethodParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcNonConstTraitMethodParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_non_const_trait_method];
|
||||
const PATH: &[Symbol] = &[sym::rustc_non_const_trait_method];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
|
|
@ -721,3 +1050,173 @@ impl<S: Stage> CombineAttributeParser<S> for RustcThenThisWouldNeedParser {
|
|||
Some(ident)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcInsignificantDtorParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcInsignificantDtorParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_insignificant_dtor];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::ForeignTy),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcInsignificantDtor;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcEffectiveVisibilityParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcEffectiveVisibilityParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_effective_visibility];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Use),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::Const),
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Closure),
|
||||
Allow(Target::Mod),
|
||||
Allow(Target::ForeignMod),
|
||||
Allow(Target::TyAlias),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Variant),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Field),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::TraitAlias),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Allow(Target::AssocConst),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::AssocTy),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ForeignTy),
|
||||
Allow(Target::MacroDef),
|
||||
Allow(Target::PatField),
|
||||
Allow(Target::Crate),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEffectiveVisibility;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcSymbolName;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcSymbolName {
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
]);
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const PATH: &[Symbol] = &[sym::rustc_symbol_name];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return None;
|
||||
}
|
||||
Some(AttributeKind::RustcSymbolName(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcDefPath;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcDefPath {
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
]);
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const PATH: &[Symbol] = &[sym::rustc_def_path];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return None;
|
||||
}
|
||||
Some(AttributeKind::RustcDefPath(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcIntrinsicParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcIntrinsicParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_intrinsic];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsic;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcIntrinsicConstStableIndirectParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcIntrinsicConstStableIndirectParser {
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_intrinsic_const_stable_indirect];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcStrictCoherenceParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcStrictCoherenceParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_strict_coherence];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::ForeignTy),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStrictCoherence;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcReservationImplParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcReservationImplParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_reservation_impl];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Impl { of_trait: true })]);
|
||||
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "reservation message");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(args.span().unwrap_or(cx.attr_span), None);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(value_str) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::RustcReservationImpl(cx.attr_span, value_str))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PreludeImportParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for PreludeImportParser {
|
||||
const PATH: &[Symbol] = &[sym::prelude_import];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Use)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PreludeImport;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,7 +244,20 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
|
|||
this.promotable = true;
|
||||
}),
|
||||
];
|
||||
const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Allow(Target::Use), // FIXME I don't think this does anything?
|
||||
Allow(Target::Const),
|
||||
Allow(Target::AssocConst),
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::Crate),
|
||||
]);
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.promotable {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use rustc_hir::attrs::RustcAbiAttrKind;
|
||||
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
|
||||
|
||||
use super::prelude::*;
|
||||
|
|
@ -140,3 +141,119 @@ impl<S: Stage> SingleAttributeParser<S> for ReexportTestHarnessMainParser {
|
|||
Some(AttributeKind::ReexportTestHarnessMain(name))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcAbiParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcAbiParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_abi];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::debug, sym::assert_eq]);
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::TyAlias),
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(args) = args.list() else {
|
||||
cx.expected_specific_argument_and_list(cx.attr_span, &[sym::assert_eq, sym::debug]);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(arg) = args.single() else {
|
||||
cx.expected_single_argument(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let fail_incorrect_argument =
|
||||
|span| cx.expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
|
||||
|
||||
let Some(arg) = arg.meta_item() else {
|
||||
fail_incorrect_argument(args.span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let kind: RustcAbiAttrKind = match arg.path().word_sym() {
|
||||
Some(sym::assert_eq) => RustcAbiAttrKind::AssertEq,
|
||||
Some(sym::debug) => RustcAbiAttrKind::Debug,
|
||||
None | Some(_) => {
|
||||
fail_incorrect_argument(arg.span());
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(AttributeKind::RustcAbi { attr_span: cx.attr_span, kind })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcDelayedBugFromInsideQueryParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcDelayedBugFromInsideQueryParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_delayed_bug_from_inside_query];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDelayedBugFromInsideQuery;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcEvaluateWhereClausesParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcEvaluateWhereClausesParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_evaluate_where_clauses];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEvaluateWhereClauses;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcOutlivesParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcOutlivesParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_outlives];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::TyAlias),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOutlives;
|
||||
}
|
||||
|
||||
pub(crate) struct TestRunnerParser;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for TestRunnerParser {
|
||||
const PATH: &[Symbol] = &[sym::test_runner];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["path"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span, args);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(single) = list.single() else {
|
||||
cx.expected_single_argument(list.span);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(meta) = single.meta_item() else {
|
||||
cx.unexpected_literal(single.span());
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(AttributeKind::TestRunner(meta.path().0.clone()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,15 +66,6 @@ impl<S: Stage> NoArgsAttributeParser<S> for ParenSugarParser {
|
|||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcParenSugar;
|
||||
}
|
||||
|
||||
pub(crate) struct TypeConstParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for TypeConstParser {
|
||||
const PATH: &[Symbol] = &[sym::type_const];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets =
|
||||
AllowedTargets::AllowList(&[Allow(Target::Const), Allow(Target::AssocConst)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::TypeConst;
|
||||
}
|
||||
|
||||
// Markers
|
||||
|
||||
pub(crate) struct MarkerParser;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
|
|
@ -61,7 +62,7 @@ use crate::target_checking::AllowedTargets;
|
|||
type GroupType<S> = LazyLock<GroupTypeInner<S>>;
|
||||
|
||||
pub(super) struct GroupTypeInner<S: Stage> {
|
||||
pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
|
||||
pub(super) accepters: BTreeMap<&'static [Symbol], GroupTypeInnerAccept<S>>,
|
||||
}
|
||||
|
||||
pub(super) struct GroupTypeInnerAccept<S: Stage> {
|
||||
|
|
@ -101,7 +102,7 @@ macro_rules! attribute_parsers {
|
|||
@[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
|
||||
) => {
|
||||
pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| {
|
||||
let mut accepters = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
|
||||
let mut accepters = BTreeMap::<_, GroupTypeInnerAccept<$stage>>::new();
|
||||
$(
|
||||
{
|
||||
thread_local! {
|
||||
|
|
@ -109,19 +110,24 @@ macro_rules! attribute_parsers {
|
|||
};
|
||||
|
||||
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
|
||||
accepters.entry(*path).or_default().push(GroupTypeInnerAccept {
|
||||
template: *template,
|
||||
accept_fn: Box::new(|cx, args| {
|
||||
STATE_OBJECT.with_borrow_mut(|s| {
|
||||
accept_fn(s, cx, args)
|
||||
})
|
||||
}),
|
||||
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
|
||||
finalizer: Box::new(|cx| {
|
||||
let state = STATE_OBJECT.take();
|
||||
state.finalize(cx)
|
||||
}),
|
||||
});
|
||||
match accepters.entry(*path) {
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(GroupTypeInnerAccept {
|
||||
template: *template,
|
||||
accept_fn: Box::new(|cx, args| {
|
||||
STATE_OBJECT.with_borrow_mut(|s| {
|
||||
accept_fn(s, cx, args)
|
||||
})
|
||||
}),
|
||||
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
|
||||
finalizer: Box::new(|cx| {
|
||||
let state = STATE_OBJECT.take();
|
||||
state.finalize(cx)
|
||||
})
|
||||
});
|
||||
}
|
||||
Entry::Occupied(_) => panic!("Attribute {path:?} has multiple accepters"),
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
|
@ -141,6 +147,7 @@ attribute_parsers!(
|
|||
DocParser,
|
||||
MacroUseParser,
|
||||
NakedParser,
|
||||
RustcCguTestAttributeParser,
|
||||
StabilityParser,
|
||||
UsedParser,
|
||||
// tidy-alphabetical-end
|
||||
|
|
@ -191,8 +198,11 @@ attribute_parsers!(
|
|||
Single<ProcMacroDeriveParser>,
|
||||
Single<RecursionLimitParser>,
|
||||
Single<ReexportTestHarnessMainParser>,
|
||||
Single<RustcAbiParser>,
|
||||
Single<RustcAllocatorZeroedVariantParser>,
|
||||
Single<RustcBuiltinMacroParser>,
|
||||
Single<RustcDefPath>,
|
||||
Single<RustcDeprecatedSafe2024Parser>,
|
||||
Single<RustcForceInlineParser>,
|
||||
Single<RustcIfThisChangedParser>,
|
||||
Single<RustcLayoutScalarValidRangeEndParser>,
|
||||
|
|
@ -200,12 +210,15 @@ attribute_parsers!(
|
|||
Single<RustcLegacyConstGenericsParser>,
|
||||
Single<RustcLintOptDenyFieldAccessParser>,
|
||||
Single<RustcMustImplementOneOfParser>,
|
||||
Single<RustcObjectLifetimeDefaultParser>,
|
||||
Single<RustcNeverTypeOptionsParser>,
|
||||
Single<RustcReservationImplParser>,
|
||||
Single<RustcScalableVectorParser>,
|
||||
Single<RustcSimdMonomorphizeLaneLimitParser>,
|
||||
Single<RustcSymbolName>,
|
||||
Single<SanitizeParser>,
|
||||
Single<ShouldPanicParser>,
|
||||
Single<SkipDuringMethodDispatchParser>,
|
||||
Single<TestRunnerParser>,
|
||||
Single<TransparencyParser>,
|
||||
Single<TypeLengthLimitParser>,
|
||||
Single<WindowsSubsystemParser>,
|
||||
|
|
@ -245,41 +258,56 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<PassByValueParser>>,
|
||||
Single<WithoutArgs<PinV2Parser>>,
|
||||
Single<WithoutArgs<PointeeParser>>,
|
||||
Single<WithoutArgs<PreludeImportParser>>,
|
||||
Single<WithoutArgs<ProcMacroAttributeParser>>,
|
||||
Single<WithoutArgs<ProcMacroParser>>,
|
||||
Single<WithoutArgs<ProfilerRuntimeParser>>,
|
||||
Single<WithoutArgs<PubTransparentParser>>,
|
||||
Single<WithoutArgs<RustcAllocatorParser>>,
|
||||
Single<WithoutArgs<RustcAllocatorZeroedParser>>,
|
||||
Single<WithoutArgs<RustcCaptureAnalysisParser>>,
|
||||
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
|
||||
Single<WithoutArgs<RustcConversionSuggestionParser>>,
|
||||
Single<WithoutArgs<RustcDeallocatorParser>>,
|
||||
Single<WithoutArgs<RustcDelayedBugFromInsideQueryParser>>,
|
||||
Single<WithoutArgs<RustcDumpDefParentsParser>>,
|
||||
Single<WithoutArgs<RustcDumpItemBoundsParser>>,
|
||||
Single<WithoutArgs<RustcDumpPredicatesParser>>,
|
||||
Single<WithoutArgs<RustcDumpUserArgsParser>>,
|
||||
Single<WithoutArgs<RustcDumpVtableParser>>,
|
||||
Single<WithoutArgs<RustcEffectiveVisibilityParser>>,
|
||||
Single<WithoutArgs<RustcEvaluateWhereClausesParser>>,
|
||||
Single<WithoutArgs<RustcHasIncoherentInherentImplsParser>>,
|
||||
Single<WithoutArgs<RustcHiddenTypeOfOpaquesParser>>,
|
||||
Single<WithoutArgs<RustcInsignificantDtorParser>>,
|
||||
Single<WithoutArgs<RustcIntrinsicConstStableIndirectParser>>,
|
||||
Single<WithoutArgs<RustcIntrinsicParser>>,
|
||||
Single<WithoutArgs<RustcLintOptTyParser>>,
|
||||
Single<WithoutArgs<RustcLintQueryInstabilityParser>>,
|
||||
Single<WithoutArgs<RustcLintUntrackedQueryInformationParser>>,
|
||||
Single<WithoutArgs<RustcMainParser>>,
|
||||
Single<WithoutArgs<RustcNeverReturnsNullPointerParser>>,
|
||||
Single<WithoutArgs<RustcNoImplicitAutorefsParser>>,
|
||||
Single<WithoutArgs<RustcNoImplicitBoundsParser>>,
|
||||
Single<WithoutArgs<RustcNoMirInlineParser>>,
|
||||
Single<WithoutArgs<RustcNonConstTraitMethodParser>>,
|
||||
Single<WithoutArgs<RustcNounwindParser>>,
|
||||
Single<WithoutArgs<RustcObjectLifetimeDefaultParser>>,
|
||||
Single<WithoutArgs<RustcOffloadKernelParser>>,
|
||||
Single<WithoutArgs<RustcOutlivesParser>>,
|
||||
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
|
||||
Single<WithoutArgs<RustcPreserveUbChecksParser>>,
|
||||
Single<WithoutArgs<RustcReallocatorParser>>,
|
||||
Single<WithoutArgs<RustcRegionsParser>>,
|
||||
Single<WithoutArgs<RustcShouldNotBeCalledOnConstItems>>,
|
||||
Single<WithoutArgs<RustcStrictCoherenceParser>>,
|
||||
Single<WithoutArgs<RustcTrivialFieldReadsParser>>,
|
||||
Single<WithoutArgs<RustcVarianceOfOpaquesParser>>,
|
||||
Single<WithoutArgs<RustcVarianceParser>>,
|
||||
Single<WithoutArgs<SpecializationTraitParser>>,
|
||||
Single<WithoutArgs<StdInternalSymbolParser>>,
|
||||
Single<WithoutArgs<ThreadLocalParser>>,
|
||||
Single<WithoutArgs<TrackCallerParser>>,
|
||||
Single<WithoutArgs<TypeConstParser>>,
|
||||
Single<WithoutArgs<UnsafeSpecializationMarkerParser>>,
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
let parts =
|
||||
n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
|
||||
if let Some(accept) = S::parsers().accepters.get(parts.as_slice()) {
|
||||
let Some(args) = ArgParser::from_attr_args(
|
||||
args,
|
||||
&parts,
|
||||
|
|
@ -368,28 +368,26 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
continue;
|
||||
}
|
||||
|
||||
for accept in accepts {
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
shared: SharedContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span,
|
||||
inner_span: lower_span(n.item.span()),
|
||||
attr_style: attr.style,
|
||||
parsed_description: ParsedDescription::Attribute,
|
||||
template: &accept.template,
|
||||
attr_path: attr_path.clone(),
|
||||
};
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
shared: SharedContext {
|
||||
cx: self,
|
||||
target_span,
|
||||
target,
|
||||
emit_lint: &mut emit_lint,
|
||||
},
|
||||
attr_span,
|
||||
inner_span: lower_span(n.item.span()),
|
||||
attr_style: attr.style,
|
||||
parsed_description: ParsedDescription::Attribute,
|
||||
template: &accept.template,
|
||||
attr_path: attr_path.clone(),
|
||||
};
|
||||
|
||||
(accept.accept_fn)(&mut cx, &args);
|
||||
finalizers.push(&accept.finalizer);
|
||||
(accept.accept_fn)(&mut cx, &args);
|
||||
finalizers.push(&accept.finalizer);
|
||||
|
||||
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
|
||||
Self::check_target(&accept.allowed_targets, target, &mut cx);
|
||||
}
|
||||
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
|
||||
Self::check_target(&accept.allowed_targets, target, &mut cx);
|
||||
}
|
||||
} else {
|
||||
// If we're here, we must be compiling a tool attribute... Or someone
|
||||
|
|
|
|||
|
|
@ -45,6 +45,15 @@ pub(crate) struct DocAliasStartEnd<'a> {
|
|||
pub attr_str: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`#[{$name})]` is missing a `{$field}` argument")]
|
||||
pub(crate) struct CguFieldsMissing<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: &'a AttrPath,
|
||||
pub field: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`#![doc({$attr_name} = \"...\")]` isn't allowed as a crate-level attribute")]
|
||||
pub(crate) struct DocAttrNotCrateLevel {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ polonius-engine = "0.13.0"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_graphviz = { path = "../rustc_graphviz" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
|
|
|
|||
|
|
@ -1,296 +0,0 @@
|
|||
borrowck_assign_due_to_use_closure =
|
||||
assignment occurs due to use in closure
|
||||
|
||||
borrowck_assign_due_to_use_coroutine =
|
||||
assign occurs due to use in coroutine
|
||||
|
||||
borrowck_assign_part_due_to_use_closure =
|
||||
assignment to part occurs due to use in closure
|
||||
|
||||
borrowck_assign_part_due_to_use_coroutine =
|
||||
assign to part occurs due to use in coroutine
|
||||
|
||||
borrowck_borrow_due_to_use_closure =
|
||||
borrow occurs due to use in closure
|
||||
|
||||
borrowck_borrow_due_to_use_coroutine =
|
||||
borrow occurs due to use in coroutine
|
||||
|
||||
borrowck_calling_operator_moves =
|
||||
calling this operator moves the value
|
||||
|
||||
borrowck_calling_operator_moves_lhs =
|
||||
calling this operator moves the left-hand side
|
||||
|
||||
borrowck_cannot_move_when_borrowed =
|
||||
cannot move out of {$place ->
|
||||
[value] value
|
||||
*[other] {$place}
|
||||
} because it is borrowed
|
||||
.label = borrow of {$borrow_place ->
|
||||
[value] value
|
||||
*[other] {$borrow_place}
|
||||
} occurs here
|
||||
.move_label = move out of {$value_place ->
|
||||
[value] value
|
||||
*[other] {$value_place}
|
||||
} occurs here
|
||||
|
||||
borrowck_capture_immute =
|
||||
capture is immutable because of use here
|
||||
|
||||
borrowck_capture_move =
|
||||
capture is moved because of use here
|
||||
|
||||
borrowck_capture_mut =
|
||||
capture is mutable because of use here
|
||||
|
||||
borrowck_closure_inferred_mut = inferred to be a `FnMut` closure
|
||||
|
||||
borrowck_closure_invoked_twice =
|
||||
closure cannot be invoked more than once because it moves the variable `{$place_name}` out of its environment
|
||||
|
||||
borrowck_closure_moved_twice =
|
||||
closure cannot be moved more than once as it is not `Copy` due to moving the variable `{$place_name}` out of its environment
|
||||
|
||||
borrowck_consider_borrow_type_contents =
|
||||
help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
|
||||
|
||||
borrowck_could_not_normalize =
|
||||
could not normalize `{$value}`
|
||||
|
||||
borrowck_could_not_prove =
|
||||
could not prove `{$predicate}`
|
||||
|
||||
borrowck_dereference_suggestion =
|
||||
dereference the return value
|
||||
|
||||
borrowck_func_take_self_moved_place =
|
||||
`{$func}` takes ownership of the receiver `self`, which moves {$place_name}
|
||||
|
||||
borrowck_generic_does_not_live_long_enough =
|
||||
`{$kind}` does not live long enough
|
||||
|
||||
borrowck_higher_ranked_lifetime_error =
|
||||
higher-ranked lifetime error
|
||||
|
||||
borrowck_higher_ranked_subtype_error =
|
||||
higher-ranked subtype error
|
||||
|
||||
borrowck_implicit_static =
|
||||
this has an implicit `'static` lifetime requirement
|
||||
|
||||
borrowck_implicit_static_introduced =
|
||||
calling this method introduces the `impl`'s `'static` requirement
|
||||
|
||||
borrowck_implicit_static_relax =
|
||||
consider relaxing the implicit `'static` requirement
|
||||
|
||||
borrowck_lifetime_constraints_error =
|
||||
lifetime may not live long enough
|
||||
|
||||
borrowck_limitations_implies_static =
|
||||
due to a current limitation of the type system, this implies a `'static` lifetime
|
||||
|
||||
borrowck_move_closure_suggestion =
|
||||
consider adding 'move' keyword before the nested closure
|
||||
|
||||
borrowck_move_out_place_here =
|
||||
{$place} is moved here
|
||||
|
||||
borrowck_move_unsized =
|
||||
cannot move a value of type `{$ty}`
|
||||
.label = the size of `{$ty}` cannot be statically determined
|
||||
|
||||
borrowck_moved_a_fn_once_in_call =
|
||||
this value implements `FnOnce`, which causes it to be moved when called
|
||||
|
||||
borrowck_moved_a_fn_once_in_call_call =
|
||||
`FnOnce` closures can only be called once
|
||||
|
||||
borrowck_moved_a_fn_once_in_call_def =
|
||||
`{$ty}` is made to be an `FnOnce` closure here
|
||||
|
||||
borrowck_moved_due_to_await =
|
||||
{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to this {$is_loop_message ->
|
||||
[true] await, in previous iteration of loop
|
||||
*[false] await
|
||||
}
|
||||
|
||||
borrowck_moved_due_to_call =
|
||||
{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to this {$is_loop_message ->
|
||||
[true] call, in previous iteration of loop
|
||||
*[false] call
|
||||
}
|
||||
|
||||
borrowck_moved_due_to_implicit_into_iter_call =
|
||||
{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to this implicit call to {$is_loop_message ->
|
||||
[true] `.into_iter()`, in previous iteration of loop
|
||||
*[false] `.into_iter()`
|
||||
}
|
||||
|
||||
borrowck_moved_due_to_method_call =
|
||||
{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to this method {$is_loop_message ->
|
||||
[true] call, in previous iteration of loop
|
||||
*[false] call
|
||||
}
|
||||
|
||||
borrowck_moved_due_to_usage_in_operator =
|
||||
{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to usage in {$is_loop_message ->
|
||||
[true] operator, in previous iteration of loop
|
||||
*[false] operator
|
||||
}
|
||||
|
||||
borrowck_opaque_type_lifetime_mismatch =
|
||||
opaque type used twice with different lifetimes
|
||||
.label = lifetime `{$arg}` used here
|
||||
.prev_lifetime_label = lifetime `{$prev}` previously used here
|
||||
.note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
|
||||
|
||||
borrowck_partial_var_move_by_use_in_closure =
|
||||
variable {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to use in closure
|
||||
|
||||
borrowck_partial_var_move_by_use_in_coroutine =
|
||||
variable {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to use in coroutine
|
||||
|
||||
borrowck_restrict_to_static =
|
||||
consider restricting the type parameter to the `'static` lifetime
|
||||
|
||||
borrowck_returned_async_block_escaped =
|
||||
returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
|
||||
|
||||
borrowck_returned_closure_escaped =
|
||||
returns a closure that contains a reference to a captured variable, which then escapes the closure body
|
||||
|
||||
borrowck_returned_lifetime_short =
|
||||
{$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}`
|
||||
|
||||
borrowck_returned_lifetime_wrong =
|
||||
{$mir_def_name} was supposed to return data with lifetime `{$outlived_fr_name}` but it is returning data with lifetime `{$fr_name}`
|
||||
|
||||
borrowck_returned_ref_escaped =
|
||||
returns a reference to a captured variable which escapes the closure body
|
||||
|
||||
borrowck_simd_intrinsic_arg_const =
|
||||
{$arg ->
|
||||
[1] 1st
|
||||
[2] 2nd
|
||||
[3] 3rd
|
||||
*[other] {$arg}th
|
||||
} argument of `{$intrinsic}` is required to be a `const` item
|
||||
|
||||
borrowck_suggest_create_fresh_reborrow =
|
||||
consider reborrowing the `Pin` instead of moving it
|
||||
|
||||
borrowck_suggest_iterate_over_slice =
|
||||
consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop
|
||||
|
||||
borrowck_tail_expr_drop_order = relative drop order changing in Rust 2024
|
||||
.label = this temporary value will be dropped at the end of the block
|
||||
.note = consider using a `let` binding to ensure the value will live long enough
|
||||
|
||||
borrowck_ty_no_impl_copy =
|
||||
{$is_partial_move ->
|
||||
[true] partial move
|
||||
*[false] move
|
||||
} occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait
|
||||
|
||||
borrowck_use_due_to_use_closure =
|
||||
use occurs due to use in closure
|
||||
|
||||
borrowck_use_due_to_use_coroutine =
|
||||
use occurs due to use in coroutine
|
||||
|
||||
borrowck_used_impl_require_static =
|
||||
the used `impl` has a `'static` requirement
|
||||
|
||||
borrowck_value_capture_here =
|
||||
value captured {$is_within ->
|
||||
[true] here by coroutine
|
||||
*[false] here
|
||||
}
|
||||
|
||||
borrowck_value_moved_here =
|
||||
value {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} {$is_move_msg ->
|
||||
[true] into closure here
|
||||
*[false] here
|
||||
}{$is_loop_message ->
|
||||
[true] , in previous iteration of loop
|
||||
*[false] {""}
|
||||
}
|
||||
|
||||
borrowck_var_borrow_by_use_in_closure =
|
||||
borrow occurs due to use in closure
|
||||
|
||||
borrowck_var_borrow_by_use_in_coroutine =
|
||||
borrow occurs due to use in coroutine
|
||||
|
||||
borrowck_var_borrow_by_use_place_in_closure =
|
||||
{$is_single_var ->
|
||||
*[true] borrow occurs
|
||||
[false] borrows occur
|
||||
} due to use of {$place} in closure
|
||||
|
||||
borrowck_var_borrow_by_use_place_in_coroutine =
|
||||
{$is_single_var ->
|
||||
*[true] borrow occurs
|
||||
[false] borrows occur
|
||||
} due to use of {$place} in coroutine
|
||||
|
||||
borrowck_var_cannot_escape_closure =
|
||||
captured variable cannot escape `FnMut` closure body
|
||||
.note = `FnMut` closures only have access to their captured variables while they are executing...
|
||||
.cannot_escape = ...therefore, they cannot allow references to captured variables to escape
|
||||
|
||||
borrowck_var_does_not_need_mut =
|
||||
variable does not need to be mutable
|
||||
.suggestion = remove this `mut`
|
||||
|
||||
borrowck_var_first_borrow_by_use_place_in_closure =
|
||||
first borrow occurs due to use of {$place} in closure
|
||||
|
||||
borrowck_var_first_borrow_by_use_place_in_coroutine =
|
||||
first borrow occurs due to use of {$place} in coroutine
|
||||
|
||||
borrowck_var_here_captured = variable captured here
|
||||
|
||||
borrowck_var_here_defined = variable defined here
|
||||
|
||||
borrowck_var_move_by_use_in_closure =
|
||||
move occurs due to use in closure
|
||||
|
||||
borrowck_var_move_by_use_in_coroutine =
|
||||
move occurs due to use in coroutine
|
||||
|
||||
borrowck_var_mutable_borrow_by_use_place_in_closure =
|
||||
mutable borrow occurs due to use of {$place} in closure
|
||||
|
||||
borrowck_var_second_borrow_by_use_place_in_closure =
|
||||
second borrow occurs due to use of {$place} in closure
|
||||
|
||||
borrowck_var_second_borrow_by_use_place_in_coroutine =
|
||||
second borrow occurs due to use of {$place} in coroutine
|
||||
|
|
@ -24,7 +24,6 @@ use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_
|
|||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::MirBorrowckCtxt;
|
||||
use crate::region_infer::values::RegionElement;
|
||||
use crate::session_diagnostics::{
|
||||
HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError,
|
||||
};
|
||||
|
|
@ -49,11 +48,12 @@ impl<'tcx> UniverseInfo<'tcx> {
|
|||
UniverseInfo::RelateTys { expected, found }
|
||||
}
|
||||
|
||||
/// Report an error where an element erroneously made its way into `placeholder`.
|
||||
pub(crate) fn report_erroneous_element(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
|
||||
placeholder: ty::PlaceholderRegion<'tcx>,
|
||||
error_element: RegionElement<'tcx>,
|
||||
error_element: Option<ty::PlaceholderRegion<'tcx>>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
) {
|
||||
match *self {
|
||||
|
|
@ -146,14 +146,14 @@ pub(crate) trait TypeOpInfo<'tcx> {
|
|||
) -> Option<Diag<'infcx>>;
|
||||
|
||||
/// Constraints require that `error_element` appear in the
|
||||
/// values of `placeholder`, but this cannot be proven to
|
||||
/// values of `placeholder`, but this cannot be proven to
|
||||
/// hold. Report an error.
|
||||
#[instrument(level = "debug", skip(self, mbcx))]
|
||||
fn report_erroneous_element(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
|
||||
placeholder: ty::PlaceholderRegion<'tcx>,
|
||||
error_element: RegionElement<'tcx>,
|
||||
error_element: Option<ty::PlaceholderRegion<'tcx>>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
) {
|
||||
let tcx = mbcx.infcx.tcx;
|
||||
|
|
@ -172,19 +172,17 @@ pub(crate) trait TypeOpInfo<'tcx> {
|
|||
ty::PlaceholderRegion::new(adjusted_universe.into(), placeholder.bound),
|
||||
);
|
||||
|
||||
let error_region =
|
||||
if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
|
||||
let adjusted_universe =
|
||||
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
|
||||
adjusted_universe.map(|adjusted| {
|
||||
ty::Region::new_placeholder(
|
||||
tcx,
|
||||
ty::PlaceholderRegion::new(adjusted.into(), error_placeholder.bound),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// FIXME: one day this should just be error_element,
|
||||
// and this method shouldn't do anything.
|
||||
let error_region = error_element.and_then(|e| {
|
||||
let adjusted_universe = e.universe.as_u32().checked_sub(base_universe.as_u32());
|
||||
adjusted_universe.map(|adjusted| {
|
||||
ty::Region::new_placeholder(
|
||||
tcx,
|
||||
ty::PlaceholderRegion::new(adjusted.into(), e.bound),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
debug!(?placeholder_region);
|
||||
|
||||
|
|
|
|||
|
|
@ -649,8 +649,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
// We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we
|
||||
// ensure that we don't emit live boring locals as explanations.
|
||||
let is_local_boring = |local| {
|
||||
if let Some(polonius_diagnostics) = self.polonius_diagnostics {
|
||||
polonius_diagnostics.boring_nll_locals.contains(&local)
|
||||
if let Some(polonius_context) = self.polonius_context {
|
||||
polonius_context.boring_nll_locals.contains(&local)
|
||||
} else {
|
||||
assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled());
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ use std::collections::BTreeMap;
|
|||
|
||||
use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
|
||||
use rustc_errors::{
|
||||
Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, inline_fluent, listify,
|
||||
};
|
||||
use rustc_hir::def::{CtorKind, Namespace};
|
||||
use rustc_hir::{
|
||||
self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
|
||||
|
|
@ -35,7 +37,6 @@ use tracing::debug;
|
|||
use super::MirBorrowckCtxt;
|
||||
use super::borrow_set::BorrowData;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::nll::ConstraintDescription;
|
||||
use crate::session_diagnostics::{
|
||||
CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
|
||||
|
|
@ -700,7 +701,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
|
||||
diag.span_note(pred.span, LIMITATION_NOTE);
|
||||
return;
|
||||
}
|
||||
for bound in bounds.iter() {
|
||||
|
|
@ -711,7 +712,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
|
||||
diag.span_note(bound.span, LIMITATION_NOTE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1312,7 +1313,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let mut span: MultiSpan = spans.clone().into();
|
||||
err.arg("ty", param_ty.to_string());
|
||||
let msg = err.dcx.eagerly_translate_to_string(
|
||||
fluent::borrowck_moved_a_fn_once_in_call_def,
|
||||
inline_fluent!("`{$ty}` is made to be an `FnOnce` closure here"),
|
||||
err.args.iter(),
|
||||
);
|
||||
err.remove_arg("ty");
|
||||
|
|
@ -1321,9 +1322,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
span.push_span_label(
|
||||
fn_call_span,
|
||||
fluent::borrowck_moved_a_fn_once_in_call,
|
||||
inline_fluent!("this value implements `FnOnce`, which causes it to be moved when called"),
|
||||
);
|
||||
err.span_note(
|
||||
span,
|
||||
inline_fluent!("`FnOnce` closures can only be called once"),
|
||||
);
|
||||
err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
|
||||
} else {
|
||||
err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
|
||||
}
|
||||
|
|
@ -1568,3 +1572,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
self.local_name(index).is_none_or(|name| name.as_str().starts_with('_'))
|
||||
}
|
||||
}
|
||||
|
||||
const LIMITATION_NOTE: DiagMessage = inline_fluent!(
|
||||
"due to a current limitation of the type system, this implies a `'static` lifetime"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -354,14 +354,71 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
|
||||
{
|
||||
if snippet.starts_with("&mut ") {
|
||||
// We don't have access to the HIR to get accurate spans, but we can
|
||||
// give a best effort structured suggestion.
|
||||
err.span_suggestion_verbose(
|
||||
source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
|
||||
"if there is only one mutable reborrow, remove the `&mut`",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
// In calls, `&mut &mut T` may be deref-coerced to `&mut T`, and
|
||||
// removing the extra `&mut` is the most direct suggestion. But for
|
||||
// pattern-matching expressions (`match`, `if let`, `while let`), that
|
||||
// can easily turn into a move, so prefer suggesting an explicit
|
||||
// reborrow via `&mut *x` instead.
|
||||
let mut in_pat_scrutinee = false;
|
||||
let mut is_deref_coerced = false;
|
||||
if let Some(expr) = self.find_expr(source_info.span) {
|
||||
let tcx = self.infcx.tcx;
|
||||
let span = expr.span.source_callsite();
|
||||
for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
|
||||
if let Node::Expr(parent_expr) = node {
|
||||
match parent_expr.kind {
|
||||
ExprKind::Match(scrutinee, ..)
|
||||
if scrutinee
|
||||
.span
|
||||
.source_callsite()
|
||||
.contains(span) =>
|
||||
{
|
||||
in_pat_scrutinee = true;
|
||||
break;
|
||||
}
|
||||
ExprKind::Let(let_expr)
|
||||
if let_expr
|
||||
.init
|
||||
.span
|
||||
.source_callsite()
|
||||
.contains(span) =>
|
||||
{
|
||||
in_pat_scrutinee = true;
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let typeck = tcx.typeck(expr.hir_id.owner.def_id);
|
||||
is_deref_coerced =
|
||||
typeck.expr_adjustments(expr).iter().any(|adj| {
|
||||
matches!(adj.kind, ty::adjustment::Adjust::Deref(_))
|
||||
});
|
||||
}
|
||||
|
||||
if in_pat_scrutinee {
|
||||
// Best-effort structured suggestion: insert `*` after `&mut `.
|
||||
err.span_suggestion_verbose(
|
||||
source_info
|
||||
.span
|
||||
.with_lo(source_info.span.lo() + BytePos(5))
|
||||
.shrink_to_lo(),
|
||||
"to reborrow the mutable reference, add `*`",
|
||||
"*",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else if is_deref_coerced {
|
||||
// We don't have access to the HIR to get accurate spans, but we
|
||||
// can give a best effort structured suggestion.
|
||||
err.span_suggestion_verbose(
|
||||
source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
|
||||
"if there is only one mutable reborrow, remove the `&mut`",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// This can occur with things like `(&mut self).foo()`.
|
||||
err.span_help(source_info.span, "try removing `&mut` here");
|
||||
|
|
@ -1695,7 +1752,7 @@ fn suggest_ampmut<'tcx>(
|
|||
&& let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
|
||||
&& let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
|
||||
&& let rhs_span_new = rhs_stmt_new.source_info.span
|
||||
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
|
||||
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span_new)
|
||||
{
|
||||
(rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Error reporting machinery for lifetime errors.
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, inline_fluent};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::GenericBound::Trait;
|
||||
use rustc_hir::QPath::Resolved;
|
||||
|
|
@ -27,16 +27,15 @@ use rustc_trait_selection::infer::InferCtxtExt;
|
|||
use rustc_trait_selection::traits::{Obligation, ObligationCtxt};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource};
|
||||
use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource};
|
||||
use crate::nll::ConstraintDescription;
|
||||
use crate::region_infer::values::RegionElement;
|
||||
use crate::region_infer::{BlameConstraint, TypeTest};
|
||||
use crate::session_diagnostics::{
|
||||
FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr,
|
||||
LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote,
|
||||
};
|
||||
use crate::universal_regions::DefiningTy;
|
||||
use crate::{MirBorrowckCtxt, borrowck_errors, fluent_generated as fluent};
|
||||
use crate::{MirBorrowckCtxt, borrowck_errors};
|
||||
|
||||
impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
|
||||
fn description(&self) -> &'static str {
|
||||
|
|
@ -104,15 +103,9 @@ pub(crate) enum RegionErrorKind<'tcx> {
|
|||
/// A generic bound failure for a type test (`T: 'a`).
|
||||
TypeTestError { type_test: TypeTest<'tcx> },
|
||||
|
||||
/// Higher-ranked subtyping error.
|
||||
BoundUniversalRegionError {
|
||||
/// The placeholder free region.
|
||||
longer_fr: RegionVid,
|
||||
/// The region element that erroneously must be outlived by `longer_fr`.
|
||||
error_element: RegionElement<'tcx>,
|
||||
/// The placeholder region.
|
||||
placeholder: ty::PlaceholderRegion<'tcx>,
|
||||
},
|
||||
/// 'p outlives 'r, which does not hold. 'p is always a placeholder
|
||||
/// and 'r is some other region.
|
||||
PlaceholderOutlivesIllegalRegion { longer_fr: RegionVid, illegally_outlived_r: RegionVid },
|
||||
|
||||
/// Any other lifetime error.
|
||||
RegionError {
|
||||
|
|
@ -265,7 +258,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
|
||||
return;
|
||||
};
|
||||
diag.span_note(*trait_span, fluent::borrowck_limitations_implies_static);
|
||||
diag.span_note(*trait_span, LIMITATION_NOTE);
|
||||
let Some(generics_fn) = tcx.hir_get_generics(self.body.source.def_id().expect_local())
|
||||
else {
|
||||
return;
|
||||
|
|
@ -298,7 +291,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
if suggestions.len() > 0 {
|
||||
suggestions.dedup();
|
||||
diag.multipart_suggestion_verbose(
|
||||
fluent::borrowck_restrict_to_static,
|
||||
inline_fluent!("consider restricting the type parameter to the `'static` lifetime"),
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
|
@ -360,28 +353,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
RegionErrorKind::BoundUniversalRegionError {
|
||||
RegionErrorKind::PlaceholderOutlivesIllegalRegion {
|
||||
longer_fr,
|
||||
placeholder,
|
||||
error_element,
|
||||
illegally_outlived_r,
|
||||
} => {
|
||||
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
|
||||
|
||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||
let cause = self
|
||||
.regioncx
|
||||
.best_blame_constraint(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::Placeholder(placeholder),
|
||||
error_vid,
|
||||
)
|
||||
.0
|
||||
.cause;
|
||||
|
||||
let universe = placeholder.universe;
|
||||
let universe_info = self.regioncx.universe_info(universe);
|
||||
|
||||
universe_info.report_erroneous_element(self, placeholder, error_element, cause);
|
||||
self.report_erroneous_rvid_reaches_placeholder(longer_fr, illegally_outlived_r)
|
||||
}
|
||||
|
||||
RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
|
||||
|
|
@ -412,6 +388,43 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
outlives_suggestion.add_suggestion(self);
|
||||
}
|
||||
|
||||
/// Report that `longer_fr: error_vid`, which doesn't hold,
|
||||
/// where `longer_fr` is a placeholder.
|
||||
fn report_erroneous_rvid_reaches_placeholder(
|
||||
&mut self,
|
||||
longer_fr: RegionVid,
|
||||
error_vid: RegionVid,
|
||||
) {
|
||||
use NllRegionVariableOrigin::*;
|
||||
|
||||
let origin_longer = self.regioncx.definitions[longer_fr].origin;
|
||||
|
||||
let Placeholder(placeholder) = origin_longer else {
|
||||
bug!("Expected {longer_fr:?} to come from placeholder!");
|
||||
};
|
||||
|
||||
// FIXME: Is throwing away the existential region really the best here?
|
||||
let error_region = match self.regioncx.definitions[error_vid].origin {
|
||||
FreeRegion | Existential { .. } => None,
|
||||
Placeholder(other_placeholder) => Some(other_placeholder),
|
||||
};
|
||||
|
||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||
let cause =
|
||||
self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid).0.cause;
|
||||
|
||||
// FIXME these methods should have better names, and also probably not be this generic.
|
||||
// FIXME note that we *throw away* the error element here! We probably want to
|
||||
// thread it through the computation further down and use it, but there currently isn't
|
||||
// anything there to receive it.
|
||||
self.regioncx.universe_info(placeholder.universe).report_erroneous_element(
|
||||
self,
|
||||
placeholder,
|
||||
error_region,
|
||||
cause,
|
||||
);
|
||||
}
|
||||
|
||||
/// Report an error because the universal region `fr` was required to outlive
|
||||
/// `outlived_fr` but it is not known to do so. For example:
|
||||
///
|
||||
|
|
@ -872,6 +885,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
for alias_ty in alias_tys {
|
||||
if alias_ty.span.desugaring_kind().is_some() {
|
||||
// Skip `async` desugaring `impl Future`.
|
||||
continue;
|
||||
}
|
||||
if let TyKind::TraitObject(_, lt) = alias_ty.kind {
|
||||
if lt.kind == hir::LifetimeKind::ImplicitObjectLifetimeDefault {
|
||||
|
|
@ -966,12 +980,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
debug!("trait spans found: {:?}", traits);
|
||||
for span in &traits {
|
||||
let mut multi_span: MultiSpan = vec![*span].into();
|
||||
multi_span.push_span_label(*span, fluent::borrowck_implicit_static);
|
||||
multi_span.push_span_label(ident.span, fluent::borrowck_implicit_static_introduced);
|
||||
multi_span.push_span_label(
|
||||
*span,
|
||||
inline_fluent!("this has an implicit `'static` lifetime requirement"),
|
||||
);
|
||||
multi_span.push_span_label(
|
||||
ident.span,
|
||||
inline_fluent!(
|
||||
"calling this method introduces the `impl`'s `'static` requirement"
|
||||
),
|
||||
);
|
||||
err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span });
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
fluent::borrowck_implicit_static_relax,
|
||||
inline_fluent!("consider relaxing the implicit `'static` requirement"),
|
||||
" + '_",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
|
@ -1134,7 +1156,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
if ocx.evaluate_obligations_error_on_ambiguity().is_empty() && count > 0 {
|
||||
diag.span_suggestion_verbose(
|
||||
tcx.hir_body(*body).value.peel_blocks().span.shrink_to_lo(),
|
||||
fluent::borrowck_dereference_suggestion,
|
||||
inline_fluent!("dereference the return value"),
|
||||
"*".repeat(count),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
|
@ -1178,7 +1200,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
if let Some(closure_span) = closure_span {
|
||||
diag.span_suggestion_verbose(
|
||||
closure_span,
|
||||
fluent::borrowck_move_closure_suggestion,
|
||||
inline_fluent!("consider adding 'move' keyword before the nested closure"),
|
||||
"move ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(assert_matches)]
|
||||
#![cfg_attr(bootstrap, feature(assert_matches))]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(default_field_values)]
|
||||
#![feature(file_buffered)]
|
||||
|
|
@ -63,10 +63,10 @@ use crate::diagnostics::{
|
|||
use crate::path_utils::*;
|
||||
use crate::place_ext::PlaceExt;
|
||||
use crate::places_conflict::{PlaceConflictBias, places_conflict};
|
||||
use crate::polonius::PoloniusContext;
|
||||
use crate::polonius::legacy::{
|
||||
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
|
||||
};
|
||||
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
|
||||
use crate::prefixes::PrefixSet;
|
||||
use crate::region_infer::RegionInferenceContext;
|
||||
use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
|
||||
|
|
@ -99,8 +99,6 @@ mod used_muts;
|
|||
/// A public API provided for the Rust compiler consumers.
|
||||
pub mod consumers;
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
/// Associate some local constants with the `'tcx` lifetime
|
||||
struct TyCtxtConsts<'tcx>(PhantomData<&'tcx ()>);
|
||||
|
||||
|
|
@ -123,6 +121,11 @@ fn mir_borrowck(
|
|||
let (input_body, _) = tcx.mir_promoted(def);
|
||||
debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
|
||||
|
||||
// We should eagerly check stalled coroutine obligations from HIR typeck.
|
||||
// Not doing so leads to silent normalization failures later, which will
|
||||
// fail to register opaque types in the next solver.
|
||||
tcx.check_coroutine_obligations(def)?;
|
||||
|
||||
let input_body: &Body<'_> = &input_body.borrow();
|
||||
if let Some(guar) = input_body.tainted_by_errors {
|
||||
debug!("Skipping borrowck because of tainted body");
|
||||
|
|
@ -421,7 +424,7 @@ fn borrowck_check_region_constraints<'tcx>(
|
|||
polonius_output,
|
||||
opt_closure_req,
|
||||
nll_errors,
|
||||
polonius_diagnostics,
|
||||
polonius_context,
|
||||
} = nll::compute_regions(
|
||||
root_cx,
|
||||
&infcx,
|
||||
|
|
@ -445,7 +448,7 @@ fn borrowck_check_region_constraints<'tcx>(
|
|||
®ioncx,
|
||||
&opt_closure_req,
|
||||
&borrow_set,
|
||||
polonius_diagnostics.as_ref(),
|
||||
polonius_context.as_ref(),
|
||||
);
|
||||
|
||||
// We also have a `#[rustc_regions]` annotation that causes us to dump
|
||||
|
|
@ -487,7 +490,7 @@ fn borrowck_check_region_constraints<'tcx>(
|
|||
polonius_output: None,
|
||||
move_errors: Vec::new(),
|
||||
diags_buffer,
|
||||
polonius_diagnostics: polonius_diagnostics.as_ref(),
|
||||
polonius_context: polonius_context.as_ref(),
|
||||
};
|
||||
struct MoveVisitor<'a, 'b, 'infcx, 'tcx> {
|
||||
ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>,
|
||||
|
|
@ -526,7 +529,7 @@ fn borrowck_check_region_constraints<'tcx>(
|
|||
move_errors: Vec::new(),
|
||||
diags_buffer,
|
||||
polonius_output: polonius_output.as_deref(),
|
||||
polonius_diagnostics: polonius_diagnostics.as_ref(),
|
||||
polonius_context: polonius_context.as_ref(),
|
||||
};
|
||||
|
||||
// Compute and report region errors, if any.
|
||||
|
|
@ -776,7 +779,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
|
|||
/// Results of Polonius analysis.
|
||||
polonius_output: Option<&'a PoloniusOutput>,
|
||||
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
|
||||
polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>,
|
||||
polonius_context: Option<&'a PoloniusContext>,
|
||||
}
|
||||
|
||||
// Check that:
|
||||
|
|
@ -1207,6 +1210,17 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
"access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
|
||||
place_span, kind
|
||||
);
|
||||
|
||||
// If the place is being mutated, then mark it as such anyway in order to suppress the
|
||||
// `unused_mut` lint, which is likely incorrect once the access place error has been
|
||||
// resolved.
|
||||
if rw == ReadOrWrite::Write(WriteKind::Mutate)
|
||||
&& let Ok(root_place) =
|
||||
self.is_mutable(place_span.0.as_ref(), is_local_mutation_allowed)
|
||||
{
|
||||
self.add_used_mut(root_place, state);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use std::str::FromStr;
|
|||
|
||||
use polonius_engine::{Algorithm, AllFacts, Output};
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::find_attr;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_middle::mir::pretty::PrettyPrintMirOptions;
|
||||
use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted};
|
||||
|
|
@ -15,17 +17,16 @@ use rustc_middle::ty::{self, TyCtxt};
|
|||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::points::DenseLocationMap;
|
||||
use rustc_session::config::MirIncludeSpans;
|
||||
use rustc_span::sym;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::consumers::RustcFacts;
|
||||
use crate::diagnostics::RegionErrors;
|
||||
use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints;
|
||||
use crate::polonius::PoloniusContext;
|
||||
use crate::polonius::legacy::{
|
||||
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
|
||||
};
|
||||
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
|
||||
use crate::region_infer::RegionInferenceContext;
|
||||
use crate::type_check::MirTypeckRegionConstraints;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
|
|
@ -46,7 +47,7 @@ pub(crate) struct NllOutput<'tcx> {
|
|||
|
||||
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g.
|
||||
/// localized typeck and liveness constraints.
|
||||
pub polonius_diagnostics: Option<PoloniusDiagnosticsContext>,
|
||||
pub polonius_context: Option<PoloniusContext>,
|
||||
}
|
||||
|
||||
/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
|
||||
|
|
@ -121,7 +122,7 @@ pub(crate) fn compute_regions<'tcx>(
|
|||
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
|
||||
constraints: MirTypeckRegionConstraints<'tcx>,
|
||||
mut polonius_facts: Option<AllFacts<RustcFacts>>,
|
||||
polonius_context: Option<PoloniusContext>,
|
||||
mut polonius_context: Option<PoloniusContext>,
|
||||
) -> NllOutput<'tcx> {
|
||||
let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
|
||||
|| infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
|
||||
|
|
@ -153,9 +154,9 @@ pub(crate) fn compute_regions<'tcx>(
|
|||
|
||||
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
|
||||
// and use them to compute loan liveness.
|
||||
let polonius_diagnostics = polonius_context.map(|polonius_context| {
|
||||
polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set)
|
||||
});
|
||||
if let Some(polonius_context) = polonius_context.as_mut() {
|
||||
polonius_context.compute_loan_liveness(&mut regioncx, body, borrow_set)
|
||||
}
|
||||
|
||||
// If requested: dump NLL facts, and run legacy polonius analysis.
|
||||
let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| {
|
||||
|
|
@ -188,7 +189,7 @@ pub(crate) fn compute_regions<'tcx>(
|
|||
polonius_output,
|
||||
opt_closure_req: closure_region_requirements,
|
||||
nll_errors,
|
||||
polonius_diagnostics,
|
||||
polonius_context,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,13 +231,13 @@ pub(super) fn dump_nll_mir<'tcx>(
|
|||
dumper.dump_mir(body);
|
||||
|
||||
// Also dump the region constraint graph as a graphviz file.
|
||||
let _: io::Result<()> = try {
|
||||
let _ = try {
|
||||
let mut file = dumper.create_dump_file("regioncx.all.dot", body)?;
|
||||
regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
|
||||
};
|
||||
|
||||
// Also dump the region constraint SCC graph as a graphviz file.
|
||||
let _: io::Result<()> = try {
|
||||
let _ = try {
|
||||
let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?;
|
||||
regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
|
||||
};
|
||||
|
|
@ -295,7 +296,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
|
|||
) {
|
||||
let tcx = infcx.tcx;
|
||||
let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
|
||||
if !tcx.has_attr(base_def_id, sym::rustc_regions) {
|
||||
if !find_attr!(tcx.get_all_attrs(base_def_id), AttributeKind::RustcRegions) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,19 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
|
||||
use rustc_index::interval::SparseIntervalMatrix;
|
||||
use rustc_middle::mir::{Body, Location};
|
||||
use rustc_middle::ty::RegionVid;
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
use crate::BorrowSet;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::dataflow::BorrowIndex;
|
||||
use crate::polonius::ConstraintDirection;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
/// A localized outlives constraint reifies the CFG location where the outlives constraint holds,
|
||||
/// within the origins themselves as if they were different from point to point: from `a: b`
|
||||
/// outlives constraints to `a@p: b@p`, where `p` is the point in the CFG.
|
||||
|
|
@ -12,32 +25,300 @@ use rustc_mir_dataflow::points::PointIndex;
|
|||
/// of `q`. These depend on the liveness of the regions at these points, as well as their
|
||||
/// variance.
|
||||
///
|
||||
/// The `source` origin at `from` flows into the `target` origin at `to`.
|
||||
///
|
||||
/// This dual of NLL's [crate::constraints::OutlivesConstraint] therefore encodes the
|
||||
/// position-dependent outlives constraints used by Polonius, to model the flow-sensitive loan
|
||||
/// propagation via reachability within a graph of localized constraints.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub(crate) struct LocalizedOutlivesConstraint {
|
||||
pub source: RegionVid,
|
||||
pub from: PointIndex,
|
||||
pub target: RegionVid,
|
||||
pub to: PointIndex,
|
||||
///
|
||||
/// That `LocalizedConstraintGraph` can create these edges on-demand during traversal, and we
|
||||
/// therefore model them as a pair of `LocalizedNode` vertices.
|
||||
///
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(super) struct LocalizedNode {
|
||||
pub region: RegionVid,
|
||||
pub point: PointIndex,
|
||||
}
|
||||
|
||||
/// A container of [LocalizedOutlivesConstraint]s that can be turned into a traversable
|
||||
/// `rustc_data_structures` graph.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub(crate) struct LocalizedOutlivesConstraintSet {
|
||||
pub outlives: Vec<LocalizedOutlivesConstraint>,
|
||||
/// The localized constraint graph indexes the physical and logical edges to lazily compute a given
|
||||
/// node's successors during traversal.
|
||||
pub(super) struct LocalizedConstraintGraph {
|
||||
/// The actual, physical, edges we have recorded for a given node. We localize them on-demand
|
||||
/// when traversing from the node to the successor region.
|
||||
edges: FxHashMap<LocalizedNode, FxIndexSet<RegionVid>>,
|
||||
|
||||
/// The logical edges representing the outlives constraints that hold at all points in the CFG,
|
||||
/// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs
|
||||
/// can be big, and we don't need to create such a physical edge for every point in the CFG.
|
||||
logical_edges: FxHashMap<RegionVid, FxIndexSet<RegionVid>>,
|
||||
}
|
||||
|
||||
impl LocalizedOutlivesConstraintSet {
|
||||
pub(crate) fn push(&mut self, constraint: LocalizedOutlivesConstraint) {
|
||||
if constraint.source == constraint.target && constraint.from == constraint.to {
|
||||
// 'a@p: 'a@p is pretty uninteresting
|
||||
return;
|
||||
}
|
||||
self.outlives.push(constraint);
|
||||
/// The visitor interface when traversing a `LocalizedConstraintGraph`.
|
||||
pub(super) trait LocalizedConstraintGraphVisitor {
|
||||
/// Callback called when traversing a given `loan` encounters a localized `node` it hasn't
|
||||
/// visited before.
|
||||
fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode) {}
|
||||
|
||||
/// Callback called when discovering a new `successor` node for the `current_node`.
|
||||
fn on_successor_discovered(&mut self, _current_node: LocalizedNode, _successor: LocalizedNode) {
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalizedConstraintGraph {
|
||||
/// Traverses the constraints and returns the indexed graph of edges per node.
|
||||
pub(super) fn new<'tcx>(
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
) -> Self {
|
||||
let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
|
||||
let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
|
||||
|
||||
for outlives_constraint in outlives_constraints {
|
||||
match outlives_constraint.locations {
|
||||
Locations::All(_) => {
|
||||
logical_edges
|
||||
.entry(outlives_constraint.sup)
|
||||
.or_default()
|
||||
.insert(outlives_constraint.sub);
|
||||
}
|
||||
|
||||
Locations::Single(location) => {
|
||||
let node = LocalizedNode {
|
||||
region: outlives_constraint.sup,
|
||||
point: liveness.point_from_location(location),
|
||||
};
|
||||
edges.entry(node).or_default().insert(outlives_constraint.sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LocalizedConstraintGraph { edges, logical_edges }
|
||||
}
|
||||
|
||||
/// Traverses the localized constraint graph per-loan, and notifies the `visitor` of discovered
|
||||
/// nodes and successors.
|
||||
pub(super) fn traverse<'tcx>(
|
||||
&self,
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
visitor: &mut impl LocalizedConstraintGraphVisitor,
|
||||
) {
|
||||
let live_regions = liveness.points();
|
||||
|
||||
let mut visited = FxHashSet::default();
|
||||
let mut stack = Vec::new();
|
||||
|
||||
// Compute reachability per loan by traversing each loan's subgraph starting from where it
|
||||
// is introduced.
|
||||
for (loan_idx, loan) in borrow_set.iter_enumerated() {
|
||||
visited.clear();
|
||||
stack.clear();
|
||||
|
||||
let start_node = LocalizedNode {
|
||||
region: loan.region,
|
||||
point: liveness.point_from_location(loan.reserve_location),
|
||||
};
|
||||
stack.push(start_node);
|
||||
|
||||
while let Some(node) = stack.pop() {
|
||||
if !visited.insert(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We've reached a node we haven't visited before.
|
||||
let location = liveness.location_from_point(node.point);
|
||||
visitor.on_node_traversed(loan_idx, node);
|
||||
|
||||
// When we find a _new_ successor, we'd like to
|
||||
// - visit it eventually,
|
||||
// - and let the generic visitor know about it.
|
||||
let mut successor_found = |succ| {
|
||||
if !visited.contains(&succ) {
|
||||
stack.push(succ);
|
||||
visitor.on_successor_discovered(node, succ);
|
||||
}
|
||||
};
|
||||
|
||||
// Then, we propagate the loan along the localized constraint graph. The outgoing
|
||||
// edges are computed lazily, from:
|
||||
// - the various physical edges present at this node,
|
||||
// - the materialized logical edges that exist virtually at all points for this
|
||||
// node's region, localized at this point.
|
||||
|
||||
// Universal regions propagate loans along the CFG, i.e. forwards only.
|
||||
let is_universal_region = universal_regions.is_universal_region(node.region);
|
||||
|
||||
// The physical edges present at this node are:
|
||||
//
|
||||
// 1. the typeck edges that flow from region to region *at this point*.
|
||||
for &succ in self.edges.get(&node).into_iter().flatten() {
|
||||
let succ = LocalizedNode { region: succ, point: node.point };
|
||||
successor_found(succ);
|
||||
}
|
||||
|
||||
// 2a. the liveness edges that flow *forward*, from this node's point to its
|
||||
// successors in the CFG.
|
||||
if body[location.block].statements.get(location.statement_index).is_some() {
|
||||
// Intra-block edges, straight line constraints from each point to its successor
|
||||
// within the same block.
|
||||
let next_point = node.point + 1;
|
||||
if let Some(succ) = compute_forward_successor(
|
||||
node.region,
|
||||
next_point,
|
||||
live_regions,
|
||||
live_region_variances,
|
||||
is_universal_region,
|
||||
) {
|
||||
successor_found(succ);
|
||||
}
|
||||
} else {
|
||||
// Inter-block edges, from the block's terminator to each successor block's
|
||||
// entry point.
|
||||
for successor_block in body[location.block].terminator().successors() {
|
||||
let next_location = Location { block: successor_block, statement_index: 0 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
if let Some(succ) = compute_forward_successor(
|
||||
node.region,
|
||||
next_point,
|
||||
live_regions,
|
||||
live_region_variances,
|
||||
is_universal_region,
|
||||
) {
|
||||
successor_found(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2b. the liveness edges that flow *backward*, from this node's point to its
|
||||
// predecessors in the CFG.
|
||||
if !is_universal_region {
|
||||
if location.statement_index > 0 {
|
||||
// Backward edges to the predecessor point in the same block.
|
||||
let previous_point = PointIndex::from(node.point.as_usize() - 1);
|
||||
if let Some(succ) = compute_backward_successor(
|
||||
node.region,
|
||||
node.point,
|
||||
previous_point,
|
||||
live_regions,
|
||||
live_region_variances,
|
||||
) {
|
||||
successor_found(succ);
|
||||
}
|
||||
} else {
|
||||
// Backward edges from the block entry point to the terminator of the
|
||||
// predecessor blocks.
|
||||
let predecessors = body.basic_blocks.predecessors();
|
||||
for &pred_block in &predecessors[location.block] {
|
||||
let previous_location = Location {
|
||||
block: pred_block,
|
||||
statement_index: body[pred_block].statements.len(),
|
||||
};
|
||||
let previous_point = liveness.point_from_location(previous_location);
|
||||
if let Some(succ) = compute_backward_successor(
|
||||
node.region,
|
||||
node.point,
|
||||
previous_point,
|
||||
live_regions,
|
||||
live_region_variances,
|
||||
) {
|
||||
successor_found(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And finally, we have the logical edges, materialized at this point.
|
||||
for &logical_succ in self.logical_edges.get(&node.region).into_iter().flatten() {
|
||||
let succ = LocalizedNode { region: logical_succ, point: node.point };
|
||||
successor_found(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the successor for the current region/point node when propagating a loan through forward
|
||||
/// edges, if applicable, according to liveness and variance.
|
||||
fn compute_forward_successor(
|
||||
region: RegionVid,
|
||||
next_point: PointIndex,
|
||||
live_regions: &SparseIntervalMatrix<RegionVid, PointIndex>,
|
||||
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
|
||||
is_universal_region: bool,
|
||||
) -> Option<LocalizedNode> {
|
||||
// 1. Universal regions are semantically live at all points.
|
||||
if is_universal_region {
|
||||
let succ = LocalizedNode { region, point: next_point };
|
||||
return Some(succ);
|
||||
}
|
||||
|
||||
// 2. Otherwise, gather the edges due to explicit region liveness, when applicable.
|
||||
if !live_regions.contains(region, next_point) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Here, `region` could be live at the current point, and is live at the next point: add a
|
||||
// constraint between them, according to variance.
|
||||
|
||||
// Note: there currently are cases related to promoted and const generics, where we don't yet
|
||||
// have variance information (possibly about temporary regions created when typeck sanitizes the
|
||||
// promoteds). Until that is done, we conservatively fallback to maximizing reachability by
|
||||
// adding a bidirectional edge here. This will not limit traversal whatsoever, and thus
|
||||
// propagate liveness when needed.
|
||||
//
|
||||
// FIXME: add the missing variance information and remove this fallback bidirectional edge.
|
||||
let direction =
|
||||
live_region_variances.get(®ion).unwrap_or(&ConstraintDirection::Bidirectional);
|
||||
|
||||
match direction {
|
||||
ConstraintDirection::Backward => {
|
||||
// Contravariant cases: loans flow in the inverse direction, but we're only interested
|
||||
// in forward successors and there are none here.
|
||||
None
|
||||
}
|
||||
ConstraintDirection::Forward | ConstraintDirection::Bidirectional => {
|
||||
// 1. For covariant cases: loans flow in the regular direction, from the current point
|
||||
// to the next point.
|
||||
// 2. For invariant cases, loans can flow in both directions, but here as well, we only
|
||||
// want the forward path of the bidirectional edge.
|
||||
Some(LocalizedNode { region, point: next_point })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the successor for the current region/point node when propagating a loan through backward
|
||||
/// edges, if applicable, according to liveness and variance.
|
||||
fn compute_backward_successor(
|
||||
region: RegionVid,
|
||||
current_point: PointIndex,
|
||||
previous_point: PointIndex,
|
||||
live_regions: &SparseIntervalMatrix<RegionVid, PointIndex>,
|
||||
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
|
||||
) -> Option<LocalizedNode> {
|
||||
// Liveness flows into the regions live at the next point. So, in a backwards view, we'll link
|
||||
// the region from the current point, if it's live there, to the previous point.
|
||||
if !live_regions.contains(region, current_point) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// FIXME: add the missing variance information and remove this fallback bidirectional edge. See
|
||||
// the same comment in `compute_forward_successor`.
|
||||
let direction =
|
||||
live_region_variances.get(®ion).unwrap_or(&ConstraintDirection::Bidirectional);
|
||||
|
||||
match direction {
|
||||
ConstraintDirection::Forward => {
|
||||
// Covariant cases: loans flow in the regular direction, but we're only interested in
|
||||
// backward successors and there are none here.
|
||||
None
|
||||
}
|
||||
ConstraintDirection::Backward | ConstraintDirection::Bidirectional => {
|
||||
// 1. For contravariant cases: loans flow in the inverse direction, from the current
|
||||
// point to the previous point.
|
||||
// 2. For invariant cases, loans can flow in both directions, but here as well, we only
|
||||
// want the backward path of the bidirectional edge.
|
||||
Some(LocalizedNode { region, point: previous_point })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ use rustc_session::config::MirIncludeSpans;
|
|||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::polonius::{
|
||||
LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext,
|
||||
};
|
||||
use crate::polonius::{LocalizedConstraintGraphVisitor, LocalizedNode, PoloniusContext};
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext};
|
||||
|
|
@ -24,7 +22,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
|||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
polonius_diagnostics: Option<&PoloniusDiagnosticsContext>,
|
||||
polonius_context: Option<&PoloniusContext>,
|
||||
) {
|
||||
let tcx = infcx.tcx;
|
||||
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
|
|
@ -33,8 +31,22 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
|||
|
||||
let Some(dumper) = MirDumper::new(tcx, "polonius", body) else { return };
|
||||
|
||||
let polonius_diagnostics =
|
||||
polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
|
||||
let polonius_context =
|
||||
polonius_context.expect("missing polonius context with `-Zpolonius=next`");
|
||||
|
||||
// If we have a polonius graph to dump along the rest of the MIR and NLL info, we extract its
|
||||
// constraints here.
|
||||
let mut collector = LocalizedOutlivesConstraintCollector { constraints: Vec::new() };
|
||||
if let Some(graph) = &polonius_context.graph {
|
||||
graph.traverse(
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
&polonius_context.live_region_variances,
|
||||
regioncx.universal_regions(),
|
||||
borrow_set,
|
||||
&mut collector,
|
||||
);
|
||||
}
|
||||
|
||||
let extra_data = &|pass_where, out: &mut dyn io::Write| {
|
||||
emit_polonius_mir(
|
||||
|
|
@ -42,7 +54,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
|||
regioncx,
|
||||
closure_region_requirements,
|
||||
borrow_set,
|
||||
&polonius_diagnostics.localized_outlives_constraints,
|
||||
&collector.constraints,
|
||||
pass_where,
|
||||
out,
|
||||
)
|
||||
|
|
@ -58,19 +70,36 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
|||
|
||||
let dumper = dumper.set_extra_data(extra_data).set_options(options);
|
||||
|
||||
let _: io::Result<()> = try {
|
||||
let _ = try {
|
||||
let mut file = dumper.create_dump_file("html", body)?;
|
||||
emit_polonius_dump(
|
||||
&dumper,
|
||||
body,
|
||||
regioncx,
|
||||
borrow_set,
|
||||
&polonius_diagnostics.localized_outlives_constraints,
|
||||
&mut file,
|
||||
)?;
|
||||
emit_polonius_dump(&dumper, body, regioncx, borrow_set, &collector.constraints, &mut file)?;
|
||||
};
|
||||
}
|
||||
|
||||
/// The constraints we'll dump as text or a mermaid graph.
|
||||
struct LocalizedOutlivesConstraint {
|
||||
source: RegionVid,
|
||||
from: PointIndex,
|
||||
target: RegionVid,
|
||||
to: PointIndex,
|
||||
}
|
||||
|
||||
/// Visitor to record constraints encountered when traversing the localized constraint graph.
|
||||
struct LocalizedOutlivesConstraintCollector {
|
||||
constraints: Vec<LocalizedOutlivesConstraint>,
|
||||
}
|
||||
|
||||
impl LocalizedConstraintGraphVisitor for LocalizedOutlivesConstraintCollector {
|
||||
fn on_successor_discovered(&mut self, current_node: LocalizedNode, successor: LocalizedNode) {
|
||||
self.constraints.push(LocalizedOutlivesConstraint {
|
||||
source: current_node.region,
|
||||
from: current_node.point,
|
||||
target: successor.region,
|
||||
to: successor.point,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// The polonius dump consists of:
|
||||
/// - the NLL MIR
|
||||
/// - the list of polonius localized constraints
|
||||
|
|
@ -82,7 +111,7 @@ fn emit_polonius_dump<'tcx>(
|
|||
body: &Body<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
localized_outlives_constraints: &[LocalizedOutlivesConstraint],
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
// Prepare the HTML dump file prologue.
|
||||
|
|
@ -193,7 +222,7 @@ fn emit_polonius_mir<'tcx>(
|
|||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
localized_outlives_constraints: &[LocalizedOutlivesConstraint],
|
||||
pass_where: PassWhere,
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
|
|
@ -212,10 +241,10 @@ fn emit_polonius_mir<'tcx>(
|
|||
// Add localized outlives constraints
|
||||
match pass_where {
|
||||
PassWhere::BeforeCFG => {
|
||||
if localized_outlives_constraints.outlives.len() > 0 {
|
||||
if localized_outlives_constraints.len() > 0 {
|
||||
writeln!(out, "| Localized constraints")?;
|
||||
|
||||
for constraint in &localized_outlives_constraints.outlives {
|
||||
for constraint in localized_outlives_constraints {
|
||||
let LocalizedOutlivesConstraint { source, from, target, to } = constraint;
|
||||
let from = liveness.location_from_point(*from);
|
||||
let to = liveness.location_from_point(*to);
|
||||
|
|
@ -399,7 +428,7 @@ fn emit_mermaid_nll_sccs<'tcx>(
|
|||
fn emit_mermaid_constraint_graph<'tcx>(
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
localized_outlives_constraints: &[LocalizedOutlivesConstraint],
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<usize> {
|
||||
let location_name = |location: Location| {
|
||||
|
|
@ -438,7 +467,7 @@ fn emit_mermaid_constraint_graph<'tcx>(
|
|||
// The regions subgraphs containing the region/point nodes.
|
||||
let mut points_per_region: FxIndexMap<RegionVid, FxIndexSet<PointIndex>> =
|
||||
FxIndexMap::default();
|
||||
for constraint in &localized_outlives_constraints.outlives {
|
||||
for constraint in localized_outlives_constraints {
|
||||
points_per_region.entry(constraint.source).or_default().insert(constraint.from);
|
||||
points_per_region.entry(constraint.target).or_default().insert(constraint.to);
|
||||
}
|
||||
|
|
@ -451,7 +480,7 @@ fn emit_mermaid_constraint_graph<'tcx>(
|
|||
}
|
||||
|
||||
// The constraint graph edges.
|
||||
for constraint in &localized_outlives_constraints.outlives {
|
||||
for constraint in localized_outlives_constraints {
|
||||
// FIXME: add killed loans and constraint kind as edge labels.
|
||||
writeln!(
|
||||
out,
|
||||
|
|
@ -463,6 +492,6 @@ fn emit_mermaid_constraint_graph<'tcx>(
|
|||
|
||||
// Return the number of edges: this is the biggest graph in the dump and its edge count will be
|
||||
// mermaid's max edge count to support.
|
||||
let edge_count = borrow_set.len() + localized_outlives_constraints.outlives.len();
|
||||
let edge_count = borrow_set.len() + localized_outlives_constraints.len();
|
||||
Ok(edge_count)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,15 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::bit_set::SparseBitMatrix;
|
||||
use rustc_middle::mir::{Body, Location};
|
||||
use rustc_middle::ty::relate::{
|
||||
self, Relate, RelateResult, TypeRelation, relate_args_with_variances,
|
||||
};
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
use super::{
|
||||
ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
|
||||
PoloniusLivenessContext,
|
||||
};
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use super::{ConstraintDirection, PoloniusContext};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
impl PoloniusLivenessContext {
|
||||
impl PoloniusContext {
|
||||
/// Record the variance of each region contained within the given value.
|
||||
pub(crate) fn record_live_region_variance<'tcx>(
|
||||
&mut self,
|
||||
|
|
@ -34,165 +27,6 @@ impl PoloniusLivenessContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
|
||||
/// constraints for loans that are propagated to the next statements.
|
||||
pub(super) fn create_liveness_constraints<'tcx>(
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
|
||||
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
||||
let statement_count = bb.statements.len();
|
||||
for statement_index in 0..=statement_count {
|
||||
let current_location = Location { block, statement_index };
|
||||
let current_point = liveness.point_from_location(current_location);
|
||||
|
||||
if statement_index < statement_count {
|
||||
// Intra-block edges, straight line constraints from each point to its successor
|
||||
// within the same block.
|
||||
let next_location = Location { block, statement_index: statement_index + 1 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
propagate_loans_between_points(
|
||||
current_point,
|
||||
next_point,
|
||||
live_regions,
|
||||
live_region_variances,
|
||||
universal_regions,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
} else {
|
||||
// Inter-block edges, from the block's terminator to each successor block's entry
|
||||
// point.
|
||||
for successor_block in bb.terminator().successors() {
|
||||
let next_location = Location { block: successor_block, statement_index: 0 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
propagate_loans_between_points(
|
||||
current_point,
|
||||
next_point,
|
||||
live_regions,
|
||||
live_region_variances,
|
||||
universal_regions,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans within a region between two points in the CFG, if that region is live at both
|
||||
/// the source and target points.
|
||||
fn propagate_loans_between_points(
|
||||
current_point: PointIndex,
|
||||
next_point: PointIndex,
|
||||
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
|
||||
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
|
||||
universal_regions: &UniversalRegions<'_>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
// Universal regions are semantically live at all points.
|
||||
// Note: we always have universal regions but they're not always (or often) involved in the
|
||||
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
|
||||
// will be disconnected from the rest of the graph and thus, unnecessary.
|
||||
//
|
||||
// FIXME: only emit the edges of universal regions that existential regions can reach.
|
||||
for region in universal_regions.universal_regions_iter() {
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: current_point,
|
||||
target: region,
|
||||
to: next_point,
|
||||
});
|
||||
}
|
||||
|
||||
let Some(next_live_regions) = live_regions.row(next_point) else {
|
||||
// There are no constraints to add: there are no live regions at the next point.
|
||||
return;
|
||||
};
|
||||
|
||||
for region in next_live_regions.iter() {
|
||||
// `region` could be live at the current point, and is live at the next point: add a
|
||||
// constraint between them, according to variance.
|
||||
if let Some(&direction) = live_region_variances.get(®ion) {
|
||||
add_liveness_constraint(
|
||||
region,
|
||||
current_point,
|
||||
next_point,
|
||||
direction,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
} else {
|
||||
// Note: there currently are cases related to promoted and const generics, where we
|
||||
// don't yet have variance information (possibly about temporary regions created when
|
||||
// typeck sanitizes the promoteds). Until that is done, we conservatively fallback to
|
||||
// maximizing reachability by adding a bidirectional edge here. This will not limit
|
||||
// traversal whatsoever, and thus propagate liveness when needed.
|
||||
//
|
||||
// FIXME: add the missing variance information and remove this fallback bidirectional
|
||||
// edge.
|
||||
let fallback = ConstraintDirection::Bidirectional;
|
||||
add_liveness_constraint(
|
||||
region,
|
||||
current_point,
|
||||
next_point,
|
||||
fallback,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds `LocalizedOutlivesConstraint`s between two connected points, according to the given edge
|
||||
/// direction.
|
||||
fn add_liveness_constraint(
|
||||
region: RegionVid,
|
||||
current_point: PointIndex,
|
||||
next_point: PointIndex,
|
||||
direction: ConstraintDirection,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
match direction {
|
||||
ConstraintDirection::Forward => {
|
||||
// Covariant cases: loans flow in the regular direction, from the current point to the
|
||||
// next point.
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: current_point,
|
||||
target: region,
|
||||
to: next_point,
|
||||
});
|
||||
}
|
||||
ConstraintDirection::Backward => {
|
||||
// Contravariant cases: loans flow in the inverse direction, from the next point to the
|
||||
// current point.
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: next_point,
|
||||
target: region,
|
||||
to: current_point,
|
||||
});
|
||||
}
|
||||
ConstraintDirection::Bidirectional => {
|
||||
// For invariant cases, loans can flow in both directions: we add both edges.
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: current_point,
|
||||
target: region,
|
||||
to: next_point,
|
||||
});
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: next_point,
|
||||
target: region,
|
||||
to: current_point,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts variances for regions contained within types. Follows the same structure as
|
||||
/// `rustc_infer`'s `Generalizer`: we try to relate a type with itself to track and extract the
|
||||
/// variances of regions.
|
||||
|
|
|
|||
|
|
@ -1,160 +0,0 @@
|
|||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
|
||||
use rustc_middle::ty::RegionVid;
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
use super::{LiveLoans, LocalizedOutlivesConstraintSet};
|
||||
use crate::BorrowSet;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
|
||||
/// Compute loan reachability to approximately trace loan liveness throughout the CFG, by
|
||||
/// traversing the full graph of constraints that combines:
|
||||
/// - the localized constraints (the physical edges),
|
||||
/// - with the constraints that hold at all points (the logical edges).
|
||||
pub(super) fn compute_loan_liveness<'tcx>(
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
) -> LiveLoans {
|
||||
let mut live_loans = LiveLoans::new(borrow_set.len());
|
||||
|
||||
// Create the full graph with the physical edges we've localized earlier, and the logical edges
|
||||
// of constraints that hold at all points.
|
||||
let logical_constraints =
|
||||
outlives_constraints.filter(|c| matches!(c.locations, Locations::All(_)));
|
||||
let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints, logical_constraints);
|
||||
let mut visited = FxHashSet::default();
|
||||
let mut stack = Vec::new();
|
||||
|
||||
// Compute reachability per loan by traversing each loan's subgraph starting from where it is
|
||||
// introduced.
|
||||
for (loan_idx, loan) in borrow_set.iter_enumerated() {
|
||||
visited.clear();
|
||||
stack.clear();
|
||||
|
||||
let start_node = LocalizedNode {
|
||||
region: loan.region,
|
||||
point: liveness.point_from_location(loan.reserve_location),
|
||||
};
|
||||
stack.push(start_node);
|
||||
|
||||
while let Some(node) = stack.pop() {
|
||||
if !visited.insert(node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Record the loan as being live on entry to this point if it reaches a live region
|
||||
// there.
|
||||
//
|
||||
// This is an approximation of liveness (which is the thing we want), in that we're
|
||||
// using a single notion of reachability to represent what used to be _two_ different
|
||||
// transitive closures. It didn't seem impactful when coming up with the single-graph
|
||||
// and reachability through space (regions) + time (CFG) concepts, but in practice the
|
||||
// combination of time-traveling with kills is more impactful than initially
|
||||
// anticipated.
|
||||
//
|
||||
// Kills should prevent a loan from reaching its successor points in the CFG, but not
|
||||
// while time-traveling: we're not actually at that CFG point, but looking for
|
||||
// predecessor regions that contain the loan. One of the two TCs we had pushed the
|
||||
// transitive subset edges to each point instead of having backward edges, and the
|
||||
// problem didn't exist before. In the abstract, naive reachability is not enough to
|
||||
// model this, we'd need a slightly different solution. For example, maybe with a
|
||||
// two-step traversal:
|
||||
// - at each point we first traverse the subgraph (and possibly time-travel) looking for
|
||||
// exit nodes while ignoring kills,
|
||||
// - and then when we're back at the current point, we continue normally.
|
||||
//
|
||||
// Another (less annoying) subtlety is that kills and the loan use-map are
|
||||
// flow-insensitive. Kills can actually appear in places before a loan is introduced, or
|
||||
// at a location that is actually unreachable in the CFG from the introduction point,
|
||||
// and these can also be encountered during time-traveling.
|
||||
//
|
||||
// The simplest change that made sense to "fix" the issues above is taking into
|
||||
// account kills that are:
|
||||
// - reachable from the introduction point
|
||||
// - encountered during forward traversal. Note that this is not transitive like the
|
||||
// two-step traversal described above: only kills encountered on exit via a backward
|
||||
// edge are ignored.
|
||||
//
|
||||
// This version of the analysis, however, is enough in practice to pass the tests that
|
||||
// we care about and NLLs reject, without regressions on crater, and is an actionable
|
||||
// subset of the full analysis. It also naturally points to areas of improvement that we
|
||||
// wish to explore later, namely handling kills appropriately during traversal, instead
|
||||
// of continuing traversal to all the reachable nodes.
|
||||
//
|
||||
// FIXME: analyze potential unsoundness, possibly in concert with a borrowck
|
||||
// implementation in a-mir-formality, fuzzing, or manually crafting counter-examples.
|
||||
|
||||
if liveness.is_live_at(node.region, liveness.location_from_point(node.point)) {
|
||||
live_loans.insert(node.point, loan_idx);
|
||||
}
|
||||
|
||||
for succ in graph.outgoing_edges(node) {
|
||||
stack.push(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
live_loans
|
||||
}
|
||||
|
||||
/// The localized constraint graph indexes the physical and logical edges to compute a given node's
|
||||
/// successors during traversal.
|
||||
struct LocalizedConstraintGraph {
|
||||
/// The actual, physical, edges we have recorded for a given node.
|
||||
edges: FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>,
|
||||
|
||||
/// The logical edges representing the outlives constraints that hold at all points in the CFG,
|
||||
/// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs
|
||||
/// can be big, and we don't need to create such a physical edge for every point in the CFG.
|
||||
logical_edges: FxHashMap<RegionVid, FxIndexSet<RegionVid>>,
|
||||
}
|
||||
|
||||
/// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
struct LocalizedNode {
|
||||
region: RegionVid,
|
||||
point: PointIndex,
|
||||
}
|
||||
|
||||
impl LocalizedConstraintGraph {
|
||||
/// Traverses the constraints and returns the indexed graph of edges per node.
|
||||
fn new<'tcx>(
|
||||
constraints: &LocalizedOutlivesConstraintSet,
|
||||
logical_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
) -> Self {
|
||||
let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
|
||||
for constraint in &constraints.outlives {
|
||||
let source = LocalizedNode { region: constraint.source, point: constraint.from };
|
||||
let target = LocalizedNode { region: constraint.target, point: constraint.to };
|
||||
edges.entry(source).or_default().insert(target);
|
||||
}
|
||||
|
||||
let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
|
||||
for constraint in logical_constraints {
|
||||
logical_edges.entry(constraint.sup).or_default().insert(constraint.sub);
|
||||
}
|
||||
|
||||
LocalizedConstraintGraph { edges, logical_edges }
|
||||
}
|
||||
|
||||
/// Returns the outgoing edges of a given node, not its transitive closure.
|
||||
fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> {
|
||||
// The outgoing edges are:
|
||||
// - the physical edges present at this node,
|
||||
// - the materialized logical edges that exist virtually at all points for this node's
|
||||
// region, localized at this point.
|
||||
let physical_edges =
|
||||
self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied());
|
||||
let materialized_edges =
|
||||
self.logical_edges.get(&node.region).into_iter().flat_map(move |targets| {
|
||||
targets
|
||||
.iter()
|
||||
.copied()
|
||||
.map(move |target| LocalizedNode { point: node.point, region: target })
|
||||
});
|
||||
physical_edges.chain(materialized_edges)
|
||||
}
|
||||
}
|
||||
|
|
@ -32,47 +32,37 @@
|
|||
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/22/polonius-part-1/>
|
||||
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/29/polonius-part-2/>
|
||||
//!
|
||||
//!
|
||||
//! Data flows like this:
|
||||
//! 1) during MIR typeck, record liveness data needed later: live region variances, as well as the
|
||||
//! usual NLL liveness data (just computed on more locals). That's the [PoloniusLivenessContext].
|
||||
//! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to
|
||||
//! the polonius shape. That's the main [PoloniusContext].
|
||||
//! 3) during region inference, that data and the NLL outlives constraints are used to create the
|
||||
//! localized outlives constraints, as described above. That's the [PoloniusDiagnosticsContext].
|
||||
//! 4) transfer this back to the main borrowck procedure: it handles computing errors and
|
||||
//! diagnostics, debugging and MIR dumping concerns.
|
||||
|
||||
mod constraints;
|
||||
mod dump;
|
||||
pub(crate) mod legacy;
|
||||
mod liveness_constraints;
|
||||
mod loan_liveness;
|
||||
mod typeck_constraints;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::bit_set::SparseBitMatrix;
|
||||
use rustc_index::interval::SparseIntervalMatrix;
|
||||
use rustc_middle::mir::{Body, Local};
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_middle::ty::RegionVid;
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
pub(crate) use self::constraints::*;
|
||||
pub(self) use self::constraints::*;
|
||||
pub(crate) use self::dump::dump_polonius_mir;
|
||||
use self::liveness_constraints::create_liveness_constraints;
|
||||
use self::loan_liveness::compute_loan_liveness;
|
||||
use self::typeck_constraints::convert_typeck_constraints;
|
||||
use crate::dataflow::BorrowIndex;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::{BorrowSet, RegionInferenceContext};
|
||||
|
||||
pub(crate) type LiveLoans = SparseBitMatrix<PointIndex, BorrowIndex>;
|
||||
|
||||
/// This struct holds the liveness data created during MIR typeck, and which will be used later in
|
||||
/// the process, to compute the polonius localized constraints.
|
||||
/// This struct holds the necessary
|
||||
/// - liveness data, created during MIR typeck, and which will be used to lazily compute the
|
||||
/// polonius localized constraints, during NLL region inference as well as MIR dumping,
|
||||
/// - data needed by the borrowck error computation and diagnostics.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct PoloniusLivenessContext {
|
||||
pub(crate) struct PoloniusContext {
|
||||
/// The graph from which we extract the localized outlives constraints.
|
||||
graph: Option<LocalizedConstraintGraph>,
|
||||
|
||||
/// The expected edge direction per live region: the kind of directed edge we'll create as
|
||||
/// liveness constraints depends on the variance of types with respect to each contained region.
|
||||
live_region_variances: BTreeMap<RegionVid, ConstraintDirection>,
|
||||
|
|
@ -84,27 +74,6 @@ pub(crate) struct PoloniusLivenessContext {
|
|||
pub(crate) boring_nll_locals: FxHashSet<Local>,
|
||||
}
|
||||
|
||||
/// This struct holds the data needed to create the Polonius localized constraints. Its data is
|
||||
/// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck.
|
||||
pub(crate) struct PoloniusContext {
|
||||
/// The liveness data we recorded during MIR typeck.
|
||||
liveness_context: PoloniusLivenessContext,
|
||||
|
||||
/// The set of regions that are live at a given point in the CFG, used to create localized
|
||||
/// outlives constraints between regions that are live at connected points in the CFG.
|
||||
live_regions: SparseBitMatrix<PointIndex, RegionVid>,
|
||||
}
|
||||
|
||||
/// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is
|
||||
/// computed from the [PoloniusContext] when computing NLL regions.
|
||||
pub(crate) struct PoloniusDiagnosticsContext {
|
||||
/// The localized outlives constraints that were computed in the main analysis.
|
||||
localized_outlives_constraints: LocalizedOutlivesConstraintSet,
|
||||
|
||||
/// The liveness data computed during MIR typeck: [PoloniusLivenessContext::boring_nll_locals].
|
||||
pub(crate) boring_nll_locals: FxHashSet<Local>,
|
||||
}
|
||||
|
||||
/// The direction a constraint can flow into. Used to create liveness constraints according to
|
||||
/// variance.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
|
|
@ -120,26 +89,6 @@ enum ConstraintDirection {
|
|||
}
|
||||
|
||||
impl PoloniusContext {
|
||||
/// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we
|
||||
/// need to transpose the "points where each region is live" matrix to a "live regions per point"
|
||||
/// matrix.
|
||||
// FIXME: avoid this conversion by always storing liveness data in this shape in the rest of
|
||||
// borrowck.
|
||||
pub(crate) fn create_from_liveness(
|
||||
liveness_context: PoloniusLivenessContext,
|
||||
num_regions: usize,
|
||||
points_per_live_region: &SparseIntervalMatrix<RegionVid, PointIndex>,
|
||||
) -> PoloniusContext {
|
||||
let mut live_regions_per_point = SparseBitMatrix::new(num_regions);
|
||||
for region in points_per_live_region.rows() {
|
||||
for point in points_per_live_region.row(region).unwrap().iter() {
|
||||
live_regions_per_point.insert(point, region);
|
||||
}
|
||||
}
|
||||
|
||||
PoloniusContext { live_regions: live_regions_per_point, liveness_context }
|
||||
}
|
||||
|
||||
/// Computes live loans using the set of loans model for `-Zpolonius=next`.
|
||||
///
|
||||
/// First, creates a constraint graph combining regions and CFG points, by:
|
||||
|
|
@ -151,44 +100,91 @@ impl PoloniusContext {
|
|||
///
|
||||
/// The constraint data will be used to compute errors and diagnostics.
|
||||
pub(crate) fn compute_loan_liveness<'tcx>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
&mut self,
|
||||
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) -> PoloniusDiagnosticsContext {
|
||||
let PoloniusLivenessContext { live_region_variances, boring_nll_locals } =
|
||||
self.liveness_context;
|
||||
) {
|
||||
let liveness = regioncx.liveness_constraints();
|
||||
|
||||
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
|
||||
convert_typeck_constraints(
|
||||
tcx,
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.outlives_constraints(),
|
||||
regioncx.universal_regions(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
// We don't need to prepare the graph (index NLL constraints, etc.) if we have no loans to
|
||||
// trace throughout localized constraints.
|
||||
if borrow_set.len() > 0 {
|
||||
// From the outlives constraints, liveness, and variances, we can compute reachability
|
||||
// on the lazy localized constraint graph to trace the liveness of loans, for the next
|
||||
// step in the chain (the NLL loan scope and active loans computations).
|
||||
let graph = LocalizedConstraintGraph::new(liveness, regioncx.outlives_constraints());
|
||||
|
||||
create_liveness_constraints(
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
&self.live_regions,
|
||||
&live_region_variances,
|
||||
regioncx.universal_regions(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
let mut live_loans = LiveLoans::new(borrow_set.len());
|
||||
let mut visitor = LoanLivenessVisitor { liveness, live_loans: &mut live_loans };
|
||||
graph.traverse(
|
||||
body,
|
||||
liveness,
|
||||
&self.live_region_variances,
|
||||
regioncx.universal_regions(),
|
||||
borrow_set,
|
||||
&mut visitor,
|
||||
);
|
||||
regioncx.record_live_loans(live_loans);
|
||||
|
||||
// Now that we have a complete graph, we can compute reachability to trace the liveness of
|
||||
// loans for the next step in the chain, the NLL loan scope and active loans computations.
|
||||
let live_loans = compute_loan_liveness(
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.outlives_constraints(),
|
||||
borrow_set,
|
||||
&localized_outlives_constraints,
|
||||
);
|
||||
regioncx.record_live_loans(live_loans);
|
||||
|
||||
PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals }
|
||||
// The graph can be traversed again during MIR dumping, so we store it here.
|
||||
self.graph = Some(graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor to record loan liveness when traversing the localized constraint graph.
|
||||
struct LoanLivenessVisitor<'a> {
|
||||
liveness: &'a LivenessValues,
|
||||
live_loans: &'a mut LiveLoans,
|
||||
}
|
||||
|
||||
impl LocalizedConstraintGraphVisitor for LoanLivenessVisitor<'_> {
|
||||
fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode) {
|
||||
// Record the loan as being live on entry to this point if it reaches a live region
|
||||
// there.
|
||||
//
|
||||
// This is an approximation of liveness (which is the thing we want), in that we're
|
||||
// using a single notion of reachability to represent what used to be _two_ different
|
||||
// transitive closures. It didn't seem impactful when coming up with the single-graph
|
||||
// and reachability through space (regions) + time (CFG) concepts, but in practice the
|
||||
// combination of time-traveling with kills is more impactful than initially
|
||||
// anticipated.
|
||||
//
|
||||
// Kills should prevent a loan from reaching its successor points in the CFG, but not
|
||||
// while time-traveling: we're not actually at that CFG point, but looking for
|
||||
// predecessor regions that contain the loan. One of the two TCs we had pushed the
|
||||
// transitive subset edges to each point instead of having backward edges, and the
|
||||
// problem didn't exist before. In the abstract, naive reachability is not enough to
|
||||
// model this, we'd need a slightly different solution. For example, maybe with a
|
||||
// two-step traversal:
|
||||
// - at each point we first traverse the subgraph (and possibly time-travel) looking for
|
||||
// exit nodes while ignoring kills,
|
||||
// - and then when we're back at the current point, we continue normally.
|
||||
//
|
||||
// Another (less annoying) subtlety is that kills and the loan use-map are
|
||||
// flow-insensitive. Kills can actually appear in places before a loan is introduced, or
|
||||
// at a location that is actually unreachable in the CFG from the introduction point,
|
||||
// and these can also be encountered during time-traveling.
|
||||
//
|
||||
// The simplest change that made sense to "fix" the issues above is taking into account
|
||||
// kills that are:
|
||||
// - reachable from the introduction point
|
||||
// - encountered during forward traversal. Note that this is not transitive like the
|
||||
// two-step traversal described above: only kills encountered on exit via a backward
|
||||
// edge are ignored.
|
||||
//
|
||||
// This version of the analysis, however, is enough in practice to pass the tests that
|
||||
// we care about and NLLs reject, without regressions on crater, and is an actionable
|
||||
// subset of the full analysis. It also naturally points to areas of improvement that we
|
||||
// wish to explore later, namely handling kills appropriately during traversal, instead
|
||||
// of continuing traversal to all the reachable nodes.
|
||||
//
|
||||
// FIXME: analyze potential unsoundness, possibly in concert with a borrowck
|
||||
// implementation in a-mir-formality, fuzzing, or manually crafting counter-examples.
|
||||
let location = self.liveness.location_from_point(node.point);
|
||||
if self.liveness.is_live_at(node.region, location) {
|
||||
self.live_loans.insert(node.point, loan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,208 +0,0 @@
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::mir::{Body, Location, Statement, StatementKind, Terminator, TerminatorKind};
|
||||
use rustc_middle::ty::{TyCtxt, TypeVisitable};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
use super::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet};
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
|
||||
/// location where effects start to be visible).
|
||||
pub(super) fn convert_typeck_constraints<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
for outlives_constraint in outlives_constraints {
|
||||
match outlives_constraint.locations {
|
||||
Locations::All(_) => {
|
||||
// We don't turn constraints holding at all points into physical edges at every
|
||||
// point in the graph. They are encoded into *traversal* instead: a given node's
|
||||
// successors will combine these logical edges with the regular, physical, localized
|
||||
// edges.
|
||||
continue;
|
||||
}
|
||||
|
||||
Locations::Single(location) => {
|
||||
// This constraint is marked as holding at one location, we localize it to that
|
||||
// location or its successor, depending on the corresponding MIR
|
||||
// statement/terminator. Unfortunately, they all show up from typeck as coming "on
|
||||
// entry", so for now we modify them to take effects that should apply "on exit"
|
||||
// into account.
|
||||
//
|
||||
// FIXME: this approach is subtle, complicated, and hard to test, so we should track
|
||||
// this information better in MIR typeck instead, for example with a new `Locations`
|
||||
// variant that contains which node is crossing over between entry and exit.
|
||||
let point = liveness.point_from_location(location);
|
||||
let localized_constraint = if let Some(stmt) =
|
||||
body[location.block].statements.get(location.statement_index)
|
||||
{
|
||||
localize_statement_constraint(
|
||||
tcx,
|
||||
stmt,
|
||||
&outlives_constraint,
|
||||
point,
|
||||
universal_regions,
|
||||
)
|
||||
} else {
|
||||
assert_eq!(location.statement_index, body[location.block].statements.len());
|
||||
let terminator = body[location.block].terminator();
|
||||
localize_terminator_constraint(
|
||||
tcx,
|
||||
body,
|
||||
terminator,
|
||||
liveness,
|
||||
&outlives_constraint,
|
||||
point,
|
||||
universal_regions,
|
||||
)
|
||||
};
|
||||
localized_outlives_constraints.push(localized_constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For a given outlives constraint arising from a MIR statement, localize the constraint with the
|
||||
/// needed CFG `from`-`to` intra-block nodes.
|
||||
fn localize_statement_constraint<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
stmt: &Statement<'tcx>,
|
||||
outlives_constraint: &OutlivesConstraint<'tcx>,
|
||||
current_point: PointIndex,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> LocalizedOutlivesConstraint {
|
||||
match &stmt.kind {
|
||||
StatementKind::Assign(box (lhs, rhs)) => {
|
||||
// To create localized outlives constraints without midpoints, we rely on the property
|
||||
// that no input regions from the RHS of the assignment will flow into themselves: they
|
||||
// should not appear in the output regions in the LHS. We believe this to be true by
|
||||
// construction of the MIR, via temporaries, and assert it here.
|
||||
//
|
||||
// We think we don't need midpoints because:
|
||||
// - every LHS Place has a unique set of regions that don't appear elsewhere
|
||||
// - this implies that for them to be part of the RHS, the same Place must be read and
|
||||
// written
|
||||
// - and that should be impossible in MIR
|
||||
//
|
||||
// When we have a more complete implementation in the future, tested with crater, etc,
|
||||
// we can remove this assertion. It's a debug assert because it can be expensive.
|
||||
debug_assert!(
|
||||
{
|
||||
let mut lhs_regions = FxHashSet::default();
|
||||
tcx.for_each_free_region(lhs, |region| {
|
||||
let region = universal_regions.to_region_vid(region);
|
||||
lhs_regions.insert(region);
|
||||
});
|
||||
|
||||
let mut rhs_regions = FxHashSet::default();
|
||||
tcx.for_each_free_region(rhs, |region| {
|
||||
let region = universal_regions.to_region_vid(region);
|
||||
rhs_regions.insert(region);
|
||||
});
|
||||
|
||||
// The intersection between LHS and RHS regions should be empty.
|
||||
lhs_regions.is_disjoint(&rhs_regions)
|
||||
},
|
||||
"there should be no common regions between the LHS and RHS of an assignment"
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// Assignments should be the only statement that can both generate constraints that
|
||||
// apply on entry (specific to the RHS place) *and* others that only apply on exit (the
|
||||
// subset of RHS regions that actually flow into the LHS): i.e., where midpoints would
|
||||
// be used to ensure the former happen before the latter, within the same MIR Location.
|
||||
}
|
||||
}
|
||||
|
||||
// We generally localize an outlives constraint to where it arises.
|
||||
LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from: current_point,
|
||||
target: outlives_constraint.sub,
|
||||
to: current_point,
|
||||
}
|
||||
}
|
||||
|
||||
/// For a given outlives constraint arising from a MIR terminator, localize the constraint with the
|
||||
/// needed CFG `from`-`to` inter-block nodes.
|
||||
fn localize_terminator_constraint<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
terminator: &Terminator<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraint: &OutlivesConstraint<'tcx>,
|
||||
current_point: PointIndex,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> LocalizedOutlivesConstraint {
|
||||
// FIXME: check if other terminators need the same handling as `Call`s, in particular
|
||||
// Assert/Yield/Drop.
|
||||
match &terminator.kind {
|
||||
// FIXME: also handle diverging calls.
|
||||
TerminatorKind::Call { destination, target: Some(target), .. } => {
|
||||
// If there is a target for the call we also relate what flows into the destination here
|
||||
// to entry to that successor.
|
||||
let destination_ty = destination.ty(&body.local_decls, tcx);
|
||||
let successor_location = Location { block: *target, statement_index: 0 };
|
||||
let successor_point = liveness.point_from_location(successor_location);
|
||||
compute_constraint_direction(
|
||||
tcx,
|
||||
outlives_constraint,
|
||||
&destination_ty,
|
||||
current_point,
|
||||
successor_point,
|
||||
universal_regions,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// Typeck constraints guide loans between regions at the current point, so we do that in
|
||||
// the general case, and liveness will take care of making them flow to the terminator's
|
||||
// successors.
|
||||
LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from: current_point,
|
||||
target: outlives_constraint.sub,
|
||||
to: current_point,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For a given outlives constraint and CFG edge, returns the localized constraint with the
|
||||
/// appropriate `from`-`to` direction. This is computed according to whether the constraint flows to
|
||||
/// or from a free region in the given `value`, some kind of result for an effectful operation, like
|
||||
/// the LHS of an assignment.
|
||||
fn compute_constraint_direction<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
outlives_constraint: &OutlivesConstraint<'tcx>,
|
||||
value: &impl TypeVisitable<TyCtxt<'tcx>>,
|
||||
current_point: PointIndex,
|
||||
successor_point: PointIndex,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> LocalizedOutlivesConstraint {
|
||||
let mut to = current_point;
|
||||
let mut from = current_point;
|
||||
tcx.for_each_free_region(value, |region| {
|
||||
let region = universal_regions.to_region_vid(region);
|
||||
if region == outlives_constraint.sub {
|
||||
// This constraint flows into the result, its effects start becoming visible on exit.
|
||||
to = successor_point;
|
||||
} else if region == outlives_constraint.sup {
|
||||
// This constraint flows from the result, its effects start becoming visible on exit.
|
||||
from = successor_point;
|
||||
}
|
||||
});
|
||||
|
||||
LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from,
|
||||
target: outlives_constraint.sub,
|
||||
to,
|
||||
}
|
||||
}
|
||||
|
|
@ -1379,11 +1379,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
.elements_contained_in(longer_fr_scc)
|
||||
.find(|e| *e != RegionElement::PlaceholderRegion(placeholder))
|
||||
{
|
||||
let illegally_outlived_r = self.region_from_element(longer_fr, &error_element);
|
||||
// Stop after the first error, it gets too noisy otherwise, and does not provide more information.
|
||||
errors_buffer.push(RegionErrorKind::BoundUniversalRegionError {
|
||||
errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion {
|
||||
longer_fr,
|
||||
error_element,
|
||||
placeholder,
|
||||
illegally_outlived_r,
|
||||
});
|
||||
} else {
|
||||
debug!("check_bound_universal_region: all bounds satisfied");
|
||||
|
|
@ -1572,7 +1572,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
|
||||
/// Get the region outlived by `longer_fr` and live at `element`.
|
||||
pub(crate) fn region_from_element(
|
||||
fn region_from_element(
|
||||
&self,
|
||||
longer_fr: RegionVid,
|
||||
element: &RegionElement<'tcx>,
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@ use rustc_span::Span;
|
|||
use crate::diagnostics::RegionName;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_move_unsized, code = E0161)]
|
||||
#[diag("cannot move a value of type `{$ty}`", code = E0161)]
|
||||
pub(crate) struct MoveUnsized<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
#[primary_span]
|
||||
#[label]
|
||||
#[label("the size of `{$ty}` cannot be statically determined")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_higher_ranked_lifetime_error)]
|
||||
#[diag("higher-ranked lifetime error")]
|
||||
pub(crate) struct HigherRankedLifetimeError {
|
||||
#[subdiagnostic]
|
||||
pub cause: Option<HigherRankedErrorCause>,
|
||||
|
|
@ -26,21 +26,21 @@ pub(crate) struct HigherRankedLifetimeError {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum HigherRankedErrorCause {
|
||||
#[note(borrowck_could_not_prove)]
|
||||
#[note("could not prove `{$predicate}`")]
|
||||
CouldNotProve { predicate: String },
|
||||
#[note(borrowck_could_not_normalize)]
|
||||
#[note("could not normalize `{$value}`")]
|
||||
CouldNotNormalize { value: String },
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_higher_ranked_subtype_error)]
|
||||
#[diag("higher-ranked subtype error")]
|
||||
pub(crate) struct HigherRankedSubtypeError {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_generic_does_not_live_long_enough)]
|
||||
#[diag("`{$kind}` does not live long enough")]
|
||||
pub(crate) struct GenericDoesNotLiveLongEnough {
|
||||
pub kind: String,
|
||||
#[primary_span]
|
||||
|
|
@ -48,15 +48,20 @@ pub(crate) struct GenericDoesNotLiveLongEnough {
|
|||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(borrowck_var_does_not_need_mut)]
|
||||
#[diag("variable does not need to be mutable")]
|
||||
pub(crate) struct VarNeedNotMut {
|
||||
#[suggestion(style = "short", applicability = "machine-applicable", code = "")]
|
||||
#[suggestion(
|
||||
"remove this `mut`",
|
||||
style = "short",
|
||||
applicability = "machine-applicable",
|
||||
code = ""
|
||||
)]
|
||||
pub span: Span,
|
||||
}
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_var_cannot_escape_closure)]
|
||||
#[note]
|
||||
#[note(borrowck_cannot_escape)]
|
||||
#[diag("captured variable cannot escape `FnMut` closure body")]
|
||||
#[note("`FnMut` closures only have access to their captured variables while they are executing...")]
|
||||
#[note("...therefore, they cannot allow references to captured variables to escape")]
|
||||
pub(crate) struct FnMutError {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
|
@ -66,17 +71,17 @@ pub(crate) struct FnMutError {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum VarHereDenote {
|
||||
#[label(borrowck_var_here_captured)]
|
||||
#[label("variable captured here")]
|
||||
Captured {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[label(borrowck_var_here_defined)]
|
||||
#[label("variable defined here")]
|
||||
Defined {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[label(borrowck_closure_inferred_mut)]
|
||||
#[label("inferred to be a `FnMut` closure")]
|
||||
FnMutInferred {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
|
|
@ -85,17 +90,21 @@ pub(crate) enum VarHereDenote {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum FnMutReturnTypeErr {
|
||||
#[label(borrowck_returned_closure_escaped)]
|
||||
#[label(
|
||||
"returns a closure that contains a reference to a captured variable, which then escapes the closure body"
|
||||
)]
|
||||
ReturnClosure {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[label(borrowck_returned_async_block_escaped)]
|
||||
#[label(
|
||||
"returns an `async` block that contains a reference to a captured variable, which then escapes the closure body"
|
||||
)]
|
||||
ReturnAsyncBlock {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[label(borrowck_returned_ref_escaped)]
|
||||
#[label("returns a reference to a captured variable which escapes the closure body")]
|
||||
ReturnRef {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
|
|
@ -103,7 +112,7 @@ pub(crate) enum FnMutReturnTypeErr {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_lifetime_constraints_error)]
|
||||
#[diag("lifetime may not live long enough")]
|
||||
pub(crate) struct LifetimeOutliveErr {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
|
@ -111,7 +120,9 @@ pub(crate) struct LifetimeOutliveErr {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum LifetimeReturnCategoryErr<'a> {
|
||||
#[label(borrowck_returned_lifetime_wrong)]
|
||||
#[label(
|
||||
"{$mir_def_name} was supposed to return data with lifetime `{$outlived_fr_name}` but it is returning data with lifetime `{$fr_name}`"
|
||||
)]
|
||||
WrongReturn {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
|
|
@ -119,7 +130,9 @@ pub(crate) enum LifetimeReturnCategoryErr<'a> {
|
|||
outlived_fr_name: RegionName,
|
||||
fr_name: &'a RegionName,
|
||||
},
|
||||
#[label(borrowck_returned_lifetime_short)]
|
||||
#[label(
|
||||
"{$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}`"
|
||||
)]
|
||||
ShortReturn {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
|
|
@ -131,7 +144,7 @@ pub(crate) enum LifetimeReturnCategoryErr<'a> {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum RequireStaticErr {
|
||||
#[note(borrowck_used_impl_require_static)]
|
||||
#[note("the used `impl` has a `'static` requirement")]
|
||||
UsedImpl {
|
||||
#[primary_span]
|
||||
multi_span: MultiSpan,
|
||||
|
|
@ -140,42 +153,42 @@ pub(crate) enum RequireStaticErr {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum CaptureVarPathUseCause {
|
||||
#[label(borrowck_borrow_due_to_use_coroutine)]
|
||||
#[label("borrow occurs due to use in coroutine")]
|
||||
BorrowInCoroutine {
|
||||
#[primary_span]
|
||||
path_span: Span,
|
||||
},
|
||||
#[label(borrowck_use_due_to_use_coroutine)]
|
||||
#[label("use occurs due to use in coroutine")]
|
||||
UseInCoroutine {
|
||||
#[primary_span]
|
||||
path_span: Span,
|
||||
},
|
||||
#[label(borrowck_assign_due_to_use_coroutine)]
|
||||
#[label("assign occurs due to use in coroutine")]
|
||||
AssignInCoroutine {
|
||||
#[primary_span]
|
||||
path_span: Span,
|
||||
},
|
||||
#[label(borrowck_assign_part_due_to_use_coroutine)]
|
||||
#[label("assign to part occurs due to use in coroutine")]
|
||||
AssignPartInCoroutine {
|
||||
#[primary_span]
|
||||
path_span: Span,
|
||||
},
|
||||
#[label(borrowck_borrow_due_to_use_closure)]
|
||||
#[label("borrow occurs due to use in closure")]
|
||||
BorrowInClosure {
|
||||
#[primary_span]
|
||||
path_span: Span,
|
||||
},
|
||||
#[label(borrowck_use_due_to_use_closure)]
|
||||
#[label("use occurs due to use in closure")]
|
||||
UseInClosure {
|
||||
#[primary_span]
|
||||
path_span: Span,
|
||||
},
|
||||
#[label(borrowck_assign_due_to_use_closure)]
|
||||
#[label("assignment occurs due to use in closure")]
|
||||
AssignInClosure {
|
||||
#[primary_span]
|
||||
path_span: Span,
|
||||
},
|
||||
#[label(borrowck_assign_part_due_to_use_closure)]
|
||||
#[label("assignment to part occurs due to use in closure")]
|
||||
AssignPartInClosure {
|
||||
#[primary_span]
|
||||
path_span: Span,
|
||||
|
|
@ -184,17 +197,17 @@ pub(crate) enum CaptureVarPathUseCause {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum CaptureVarKind {
|
||||
#[label(borrowck_capture_immute)]
|
||||
#[label("capture is immutable because of use here")]
|
||||
Immut {
|
||||
#[primary_span]
|
||||
kind_span: Span,
|
||||
},
|
||||
#[label(borrowck_capture_mut)]
|
||||
#[label("capture is mutable because of use here")]
|
||||
Mut {
|
||||
#[primary_span]
|
||||
kind_span: Span,
|
||||
},
|
||||
#[label(borrowck_capture_move)]
|
||||
#[label("capture is moved because of use here")]
|
||||
Move {
|
||||
#[primary_span]
|
||||
kind_span: Span,
|
||||
|
|
@ -203,77 +216,97 @@ pub(crate) enum CaptureVarKind {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum CaptureVarCause {
|
||||
#[label(borrowck_var_borrow_by_use_place_in_coroutine)]
|
||||
#[label(
|
||||
"{$is_single_var ->
|
||||
*[true] borrow occurs
|
||||
[false] borrows occur
|
||||
} due to use of {$place} in coroutine"
|
||||
)]
|
||||
BorrowUsePlaceCoroutine {
|
||||
is_single_var: bool,
|
||||
place: String,
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_borrow_by_use_place_in_closure)]
|
||||
#[label(
|
||||
"{$is_single_var ->
|
||||
*[true] borrow occurs
|
||||
[false] borrows occur
|
||||
} due to use of {$place} in closure"
|
||||
)]
|
||||
BorrowUsePlaceClosure {
|
||||
is_single_var: bool,
|
||||
place: String,
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_borrow_by_use_in_coroutine)]
|
||||
#[label("borrow occurs due to use in coroutine")]
|
||||
BorrowUseInCoroutine {
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_borrow_by_use_in_closure)]
|
||||
#[label("borrow occurs due to use in closure")]
|
||||
BorrowUseInClosure {
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_move_by_use_in_coroutine)]
|
||||
#[label("move occurs due to use in coroutine")]
|
||||
MoveUseInCoroutine {
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_move_by_use_in_closure)]
|
||||
#[label("move occurs due to use in closure")]
|
||||
MoveUseInClosure {
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_first_borrow_by_use_place_in_coroutine)]
|
||||
#[label("first borrow occurs due to use of {$place} in coroutine")]
|
||||
FirstBorrowUsePlaceCoroutine {
|
||||
place: String,
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_first_borrow_by_use_place_in_closure)]
|
||||
#[label("first borrow occurs due to use of {$place} in closure")]
|
||||
FirstBorrowUsePlaceClosure {
|
||||
place: String,
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_second_borrow_by_use_place_in_coroutine)]
|
||||
#[label("second borrow occurs due to use of {$place} in coroutine")]
|
||||
SecondBorrowUsePlaceCoroutine {
|
||||
place: String,
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_second_borrow_by_use_place_in_closure)]
|
||||
#[label("second borrow occurs due to use of {$place} in closure")]
|
||||
SecondBorrowUsePlaceClosure {
|
||||
place: String,
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_var_mutable_borrow_by_use_place_in_closure)]
|
||||
#[label("mutable borrow occurs due to use of {$place} in closure")]
|
||||
MutableBorrowUsePlaceClosure {
|
||||
place: String,
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[label(borrowck_partial_var_move_by_use_in_coroutine)]
|
||||
#[label(
|
||||
"variable {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to use in coroutine"
|
||||
)]
|
||||
PartialMoveUseInCoroutine {
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
is_partial: bool,
|
||||
},
|
||||
#[label(borrowck_partial_var_move_by_use_in_closure)]
|
||||
#[label(
|
||||
"variable {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to use in closure"
|
||||
)]
|
||||
PartialMoveUseInClosure {
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
|
|
@ -282,34 +315,57 @@ pub(crate) enum CaptureVarCause {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_cannot_move_when_borrowed, code = E0505)]
|
||||
#[diag("cannot move out of {$place ->
|
||||
[value] value
|
||||
*[other] {$place}
|
||||
} because it is borrowed", code = E0505)]
|
||||
pub(crate) struct MoveBorrow<'a> {
|
||||
pub place: &'a str,
|
||||
pub borrow_place: &'a str,
|
||||
pub value_place: &'a str,
|
||||
#[primary_span]
|
||||
#[label(borrowck_move_label)]
|
||||
#[label(
|
||||
"move out of {$value_place ->
|
||||
[value] value
|
||||
*[other] {$value_place}
|
||||
} occurs here"
|
||||
)]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
#[label(
|
||||
"borrow of {$borrow_place ->
|
||||
[value] value
|
||||
*[other] {$borrow_place}
|
||||
} occurs here"
|
||||
)]
|
||||
pub borrow_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_opaque_type_lifetime_mismatch)]
|
||||
#[diag("opaque type used twice with different lifetimes")]
|
||||
pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> {
|
||||
pub arg: GenericArg<'tcx>,
|
||||
pub prev: GenericArg<'tcx>,
|
||||
#[primary_span]
|
||||
#[label]
|
||||
#[note]
|
||||
#[label("lifetime `{$arg}` used here")]
|
||||
#[note(
|
||||
"if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types"
|
||||
)]
|
||||
pub span: Span,
|
||||
#[label(borrowck_prev_lifetime_label)]
|
||||
#[label("lifetime `{$prev}` previously used here")]
|
||||
pub prev_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum CaptureReasonLabel<'a> {
|
||||
#[label(borrowck_moved_due_to_call)]
|
||||
#[label(
|
||||
"{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to this {$is_loop_message ->
|
||||
[true] call, in previous iteration of loop
|
||||
*[false] call
|
||||
}"
|
||||
)]
|
||||
Call {
|
||||
#[primary_span]
|
||||
fn_call_span: Span,
|
||||
|
|
@ -317,7 +373,15 @@ pub(crate) enum CaptureReasonLabel<'a> {
|
|||
is_partial: bool,
|
||||
is_loop_message: bool,
|
||||
},
|
||||
#[label(borrowck_moved_due_to_usage_in_operator)]
|
||||
#[label(
|
||||
"{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to usage in {$is_loop_message ->
|
||||
[true] operator, in previous iteration of loop
|
||||
*[false] operator
|
||||
}"
|
||||
)]
|
||||
OperatorUse {
|
||||
#[primary_span]
|
||||
fn_call_span: Span,
|
||||
|
|
@ -325,7 +389,15 @@ pub(crate) enum CaptureReasonLabel<'a> {
|
|||
is_partial: bool,
|
||||
is_loop_message: bool,
|
||||
},
|
||||
#[label(borrowck_moved_due_to_implicit_into_iter_call)]
|
||||
#[label(
|
||||
"{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to this implicit call to {$is_loop_message ->
|
||||
[true] `.into_iter()`, in previous iteration of loop
|
||||
*[false] `.into_iter()`
|
||||
}"
|
||||
)]
|
||||
ImplicitCall {
|
||||
#[primary_span]
|
||||
fn_call_span: Span,
|
||||
|
|
@ -333,7 +405,15 @@ pub(crate) enum CaptureReasonLabel<'a> {
|
|||
is_partial: bool,
|
||||
is_loop_message: bool,
|
||||
},
|
||||
#[label(borrowck_moved_due_to_method_call)]
|
||||
#[label(
|
||||
"{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to this method {$is_loop_message ->
|
||||
[true] call, in previous iteration of loop
|
||||
*[false] call
|
||||
}"
|
||||
)]
|
||||
MethodCall {
|
||||
#[primary_span]
|
||||
fn_call_span: Span,
|
||||
|
|
@ -341,7 +421,15 @@ pub(crate) enum CaptureReasonLabel<'a> {
|
|||
is_partial: bool,
|
||||
is_loop_message: bool,
|
||||
},
|
||||
#[label(borrowck_moved_due_to_await)]
|
||||
#[label(
|
||||
"{$place_name} {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} due to this {$is_loop_message ->
|
||||
[true] await, in previous iteration of loop
|
||||
*[false] await
|
||||
}"
|
||||
)]
|
||||
Await {
|
||||
#[primary_span]
|
||||
fn_call_span: Span,
|
||||
|
|
@ -349,7 +437,18 @@ pub(crate) enum CaptureReasonLabel<'a> {
|
|||
is_partial: bool,
|
||||
is_loop_message: bool,
|
||||
},
|
||||
#[label(borrowck_value_moved_here)]
|
||||
#[label(
|
||||
"value {$is_partial ->
|
||||
[true] partially moved
|
||||
*[false] moved
|
||||
} {$is_move_msg ->
|
||||
[true] into closure here
|
||||
*[false] here
|
||||
}{$is_loop_message ->
|
||||
[true] , in previous iteration of loop
|
||||
*[false] {\"\"}
|
||||
}"
|
||||
)]
|
||||
MovedHere {
|
||||
#[primary_span]
|
||||
move_span: Span,
|
||||
|
|
@ -357,7 +456,7 @@ pub(crate) enum CaptureReasonLabel<'a> {
|
|||
is_move_msg: bool,
|
||||
is_loop_message: bool,
|
||||
},
|
||||
#[label(borrowck_consider_borrow_type_contents)]
|
||||
#[label("help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents")]
|
||||
BorrowContent {
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
|
|
@ -366,22 +465,22 @@ pub(crate) enum CaptureReasonLabel<'a> {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum CaptureReasonNote {
|
||||
#[note(borrowck_moved_a_fn_once_in_call)]
|
||||
#[note("this value implements `FnOnce`, which causes it to be moved when called")]
|
||||
FnOnceMoveInCall {
|
||||
#[primary_span]
|
||||
var_span: Span,
|
||||
},
|
||||
#[note(borrowck_calling_operator_moves)]
|
||||
#[note("calling this operator moves the value")]
|
||||
UnOpMoveByOperator {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[note(borrowck_calling_operator_moves_lhs)]
|
||||
#[note("calling this operator moves the left-hand side")]
|
||||
LhsMoveByOperator {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[note(borrowck_func_take_self_moved_place)]
|
||||
#[note("`{$func}` takes ownership of the receiver `self`, which moves {$place_name}")]
|
||||
FuncTakeSelf {
|
||||
func: String,
|
||||
place_name: String,
|
||||
|
|
@ -393,7 +492,7 @@ pub(crate) enum CaptureReasonNote {
|
|||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum CaptureReasonSuggest<'tcx> {
|
||||
#[suggestion(
|
||||
borrowck_suggest_iterate_over_slice,
|
||||
"consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop",
|
||||
applicability = "maybe-incorrect",
|
||||
code = "&",
|
||||
style = "verbose"
|
||||
|
|
@ -404,7 +503,7 @@ pub(crate) enum CaptureReasonSuggest<'tcx> {
|
|||
span: Span,
|
||||
},
|
||||
#[suggestion(
|
||||
borrowck_suggest_create_fresh_reborrow,
|
||||
"consider reborrowing the `Pin` instead of moving it",
|
||||
applicability = "maybe-incorrect",
|
||||
code = ".as_mut()",
|
||||
style = "verbose"
|
||||
|
|
@ -417,13 +516,18 @@ pub(crate) enum CaptureReasonSuggest<'tcx> {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum CaptureArgLabel {
|
||||
#[label(borrowck_value_capture_here)]
|
||||
#[label(
|
||||
"value captured {$is_within ->
|
||||
[true] here by coroutine
|
||||
*[false] here
|
||||
}"
|
||||
)]
|
||||
Capture {
|
||||
is_within: bool,
|
||||
#[primary_span]
|
||||
args_span: Span,
|
||||
},
|
||||
#[label(borrowck_move_out_place_here)]
|
||||
#[label("{$place} is moved here")]
|
||||
MoveOutPlace {
|
||||
place: String,
|
||||
#[primary_span]
|
||||
|
|
@ -433,13 +537,17 @@ pub(crate) enum CaptureArgLabel {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum OnClosureNote<'a> {
|
||||
#[note(borrowck_closure_invoked_twice)]
|
||||
#[note(
|
||||
"closure cannot be invoked more than once because it moves the variable `{$place_name}` out of its environment"
|
||||
)]
|
||||
InvokedTwice {
|
||||
place_name: &'a str,
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[note(borrowck_closure_moved_twice)]
|
||||
#[note(
|
||||
"closure cannot be moved more than once as it is not `Copy` due to moving the variable `{$place_name}` out of its environment"
|
||||
)]
|
||||
MovedTwice {
|
||||
place_name: &'a str,
|
||||
#[primary_span]
|
||||
|
|
@ -449,7 +557,12 @@ pub(crate) enum OnClosureNote<'a> {
|
|||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum TypeNoCopy<'a, 'tcx> {
|
||||
#[label(borrowck_ty_no_impl_copy)]
|
||||
#[label(
|
||||
"{$is_partial_move ->
|
||||
[true] partial move
|
||||
*[false] move
|
||||
} occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait"
|
||||
)]
|
||||
Label {
|
||||
is_partial_move: bool,
|
||||
ty: Ty<'tcx>,
|
||||
|
|
@ -457,12 +570,24 @@ pub(crate) enum TypeNoCopy<'a, 'tcx> {
|
|||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[note(borrowck_ty_no_impl_copy)]
|
||||
#[note(
|
||||
"{$is_partial_move ->
|
||||
[true] partial move
|
||||
*[false] move
|
||||
} occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait"
|
||||
)]
|
||||
Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str },
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_simd_intrinsic_arg_const)]
|
||||
#[diag(
|
||||
"{$arg ->
|
||||
[1] 1st
|
||||
[2] 2nd
|
||||
[3] 3rd
|
||||
*[other] {$arg}th
|
||||
} argument of `{$intrinsic}` is required to be a `const` item"
|
||||
)]
|
||||
pub(crate) struct SimdIntrinsicArgConst {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
|
@ -471,8 +596,8 @@ pub(crate) struct SimdIntrinsicArgConst {
|
|||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(borrowck_tail_expr_drop_order)]
|
||||
#[diag("relative drop order changing in Rust 2024")]
|
||||
pub(crate) struct TailExprDropOrder {
|
||||
#[label]
|
||||
#[label("this temporary value will be dropped at the end of the block")]
|
||||
pub borrowed: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use tracing::debug;
|
|||
|
||||
use super::TypeChecker;
|
||||
use crate::constraints::OutlivesConstraintSet;
|
||||
use crate::polonius::PoloniusLivenessContext;
|
||||
use crate::polonius::PoloniusContext;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ pub(super) fn generate<'tcx>(
|
|||
if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
let (_, boring_locals) =
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
|
||||
typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals =
|
||||
typeck.polonius_context.as_mut().unwrap().boring_nll_locals =
|
||||
boring_locals.into_iter().collect();
|
||||
free_regions = typeck.universal_regions.universal_regions_iter().collect();
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ pub(super) fn generate<'tcx>(
|
|||
typeck.tcx(),
|
||||
&mut typeck.constraints.liveness_constraints,
|
||||
&typeck.universal_regions,
|
||||
&mut typeck.polonius_liveness,
|
||||
&mut typeck.polonius_context,
|
||||
typeck.body,
|
||||
);
|
||||
}
|
||||
|
|
@ -140,11 +140,11 @@ fn record_regular_live_regions<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
liveness_constraints: &mut LivenessValues,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
polonius_liveness: &mut Option<PoloniusLivenessContext>,
|
||||
polonius_context: &mut Option<PoloniusContext>,
|
||||
body: &Body<'tcx>,
|
||||
) {
|
||||
let mut visitor =
|
||||
LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_liveness };
|
||||
LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_context };
|
||||
for (bb, data) in body.basic_blocks.iter_enumerated() {
|
||||
visitor.visit_basic_block_data(bb, data);
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@ struct LiveVariablesVisitor<'a, 'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
liveness_constraints: &'a mut LivenessValues,
|
||||
universal_regions: &'a UniversalRegions<'tcx>,
|
||||
polonius_liveness: &'a mut Option<PoloniusLivenessContext>,
|
||||
polonius_context: &'a mut Option<PoloniusContext>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> {
|
||||
|
|
@ -207,8 +207,8 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> {
|
|||
});
|
||||
|
||||
// When using `-Zpolonius=next`, we record the variance of each live region.
|
||||
if let Some(polonius_liveness) = self.polonius_liveness {
|
||||
polonius_liveness.record_live_region_variance(self.tcx, self.universal_regions, value);
|
||||
if let Some(polonius_context) = self.polonius_context {
|
||||
polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -622,8 +622,8 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> {
|
|||
});
|
||||
|
||||
// When using `-Zpolonius=next`, we record the variance of each live region.
|
||||
if let Some(polonius_liveness) = typeck.polonius_liveness.as_mut() {
|
||||
polonius_liveness.record_live_region_variance(
|
||||
if let Some(polonius_context) = typeck.polonius_context.as_mut() {
|
||||
polonius_context.record_live_region_variance(
|
||||
typeck.infcx.tcx,
|
||||
typeck.universal_regions,
|
||||
value,
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ use tracing::{debug, instrument, trace};
|
|||
use crate::borrow_set::BorrowSet;
|
||||
use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet};
|
||||
use crate::diagnostics::UniverseInfo;
|
||||
use crate::polonius::PoloniusContext;
|
||||
use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable};
|
||||
use crate::polonius::{PoloniusContext, PoloniusLivenessContext};
|
||||
use crate::region_infer::TypeTest;
|
||||
use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices};
|
||||
use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst};
|
||||
|
|
@ -139,8 +139,8 @@ pub(crate) fn type_check<'tcx>(
|
|||
|
||||
debug!(?normalized_inputs_and_output);
|
||||
|
||||
let polonius_liveness = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
Some(PoloniusLivenessContext::default())
|
||||
let polonius_context = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
Some(PoloniusContext::default())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
@ -162,7 +162,7 @@ pub(crate) fn type_check<'tcx>(
|
|||
borrow_set,
|
||||
constraints: &mut constraints,
|
||||
deferred_closure_requirements: &mut deferred_closure_requirements,
|
||||
polonius_liveness,
|
||||
polonius_context,
|
||||
};
|
||||
|
||||
typeck.check_user_type_annotations();
|
||||
|
|
@ -172,14 +172,7 @@ pub(crate) fn type_check<'tcx>(
|
|||
|
||||
liveness::generate(&mut typeck, &location_map, move_data);
|
||||
|
||||
// We're done with typeck, we can finalize the polonius liveness context for region inference.
|
||||
let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| {
|
||||
PoloniusContext::create_from_liveness(
|
||||
liveness_context,
|
||||
infcx.num_region_vars(),
|
||||
typeck.constraints.liveness_constraints.points(),
|
||||
)
|
||||
});
|
||||
let polonius_context = typeck.polonius_context;
|
||||
|
||||
// In case type check encountered an error region, we suppress unhelpful extra
|
||||
// errors in by clearing out all outlives bounds that we may end up checking.
|
||||
|
|
@ -238,7 +231,7 @@ struct TypeChecker<'a, 'tcx> {
|
|||
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
|
||||
deferred_closure_requirements: &'a mut DeferredClosureRequirements<'tcx>,
|
||||
/// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints.
|
||||
polonius_liveness: Option<PoloniusLivenessContext>,
|
||||
polonius_context: Option<PoloniusContext>,
|
||||
}
|
||||
|
||||
/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ pub(crate) fn expand(
|
|||
|
||||
// Generate anonymous constant serving as container for the allocator methods.
|
||||
let const_ty = ecx.ty(sig_span, TyKind::Tup(ThinVec::new()));
|
||||
let const_body = ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts)));
|
||||
let const_body = ast::ConstItemRhsKind::new_body(ecx.expr_block(ecx.block(span, stmts)));
|
||||
let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
|
||||
let const_item = if is_stmt {
|
||||
Annotatable::Stmt(Box::new(ecx.stmt_item(span, const_item)))
|
||||
|
|
|
|||
|
|
@ -288,6 +288,18 @@ fn expand_preparsed_asm(
|
|||
let msg = "asm template must be a string literal";
|
||||
let template_sp = template_expr.span;
|
||||
let template_is_mac_call = matches!(template_expr.kind, ast::ExprKind::MacCall(_));
|
||||
|
||||
// Gets the span inside `template_sp` corresponding to the given range
|
||||
let span_in_template = |range: std::ops::Range<usize>| -> Span {
|
||||
if template_is_mac_call {
|
||||
// When the template is a macro call we can't reliably get inner spans
|
||||
// so just use the entire template span (see ICEs #129503, #131292)
|
||||
template_sp
|
||||
} else {
|
||||
template_sp.from_inner(InnerSpan::new(range.start, range.end))
|
||||
}
|
||||
};
|
||||
|
||||
let ExprToSpannedString {
|
||||
symbol: template_str,
|
||||
style: template_style,
|
||||
|
|
@ -382,13 +394,8 @@ fn expand_preparsed_asm(
|
|||
|
||||
if !parser.errors.is_empty() {
|
||||
let err = parser.errors.remove(0);
|
||||
let err_sp = if template_is_mac_call {
|
||||
// If the template is a macro call we can't reliably point to the error's
|
||||
// span so just use the template's span as the error span (fixes #129503)
|
||||
template_span
|
||||
} else {
|
||||
template_span.from_inner(InnerSpan::new(err.span.start, err.span.end))
|
||||
};
|
||||
|
||||
let err_sp = span_in_template(err.span);
|
||||
|
||||
let msg = format!("invalid asm template string: {}", err.description);
|
||||
let mut e = ecx.dcx().struct_span_err(err_sp, msg);
|
||||
|
|
@ -397,8 +404,7 @@ fn expand_preparsed_asm(
|
|||
e.note(note);
|
||||
}
|
||||
if let Some((label, span)) = err.secondary_label {
|
||||
let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end));
|
||||
e.span_label(err_sp, label);
|
||||
e.span_label(span_in_template(span), label);
|
||||
}
|
||||
let guar = e.emit();
|
||||
return ExpandResult::Ready(Err(guar));
|
||||
|
|
@ -477,8 +483,7 @@ fn expand_preparsed_asm(
|
|||
ecx.dcx()
|
||||
.create_err(errors::AsmNoMatchedArgumentName {
|
||||
name: name.to_owned(),
|
||||
span: template_span
|
||||
.from_inner(InnerSpan::new(span.start, span.end)),
|
||||
span: span_in_template(span),
|
||||
})
|
||||
.emit();
|
||||
None
|
||||
|
|
@ -490,11 +495,7 @@ fn expand_preparsed_asm(
|
|||
let mut chars = arg.format.ty.chars();
|
||||
let mut modifier = chars.next();
|
||||
if chars.next().is_some() {
|
||||
let span = arg
|
||||
.format
|
||||
.ty_span
|
||||
.map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end)))
|
||||
.unwrap_or(template_sp);
|
||||
let span = arg.format.ty_span.map(span_in_template).unwrap_or(template_sp);
|
||||
ecx.dcx().emit_err(errors::AsmModifierInvalid { span });
|
||||
modifier = None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{Expr, ast};
|
||||
use rustc_attr_parsing as attr;
|
||||
use rustc_attr_parsing::{
|
||||
CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select,
|
||||
};
|
||||
use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select};
|
||||
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
|
||||
use rustc_span::{Ident, Span, sym};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
|
||||
use crate::errors::CfgSelectNoMatches;
|
||||
|
||||
/// This intermediate structure is used to emit parse errors for the branches that are not chosen.
|
||||
/// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only
|
||||
|
|
@ -75,18 +73,6 @@ pub(super) fn expand_cfg_select<'cx>(
|
|||
ecx.current_expansion.lint_node_id,
|
||||
) {
|
||||
Ok(mut branches) => {
|
||||
if let Some((underscore, _, _)) = branches.wildcard {
|
||||
// Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
|
||||
for (predicate, _, _) in &branches.unreachable {
|
||||
let span = match predicate {
|
||||
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
|
||||
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
|
||||
};
|
||||
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
|
||||
ecx.dcx().emit_warn(err);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| {
|
||||
matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True)
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -540,6 +540,7 @@ impl<'a> TraitDef<'a> {
|
|||
.filter(|a| {
|
||||
a.has_any_name(&[
|
||||
sym::allow,
|
||||
sym::expect,
|
||||
sym::warn,
|
||||
sym::deny,
|
||||
sym::forbid,
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ fn generate_default_impl(
|
|||
span,
|
||||
underscore,
|
||||
unit,
|
||||
ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts))),
|
||||
ast::ConstItemRhsKind::new_body(ecx.expr_block(ecx.block(span, stmts))),
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -678,6 +678,18 @@ pub(crate) enum InvalidFormatStringSuggestion {
|
|||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
|
||||
#[suggestion(
|
||||
"use rust debug printing macro",
|
||||
code = "{replacement}",
|
||||
style = "verbose",
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
UseRustDebugPrintingMacro {
|
||||
#[primary_span]
|
||||
macro_span: Span,
|
||||
replacement: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -990,20 +1002,6 @@ pub(crate) struct AsmUnsupportedClobberAbi {
|
|||
pub(crate) macro_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`test_runner` argument must be a path")]
|
||||
pub(crate) struct TestRunnerInvalid {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`#![test_runner(..)]` accepts exactly 1 argument")]
|
||||
pub(crate) struct TestRunnerNargs {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("expected token: `,`")]
|
||||
pub(crate) struct ExpectedCommaInList {
|
||||
|
|
@ -1086,17 +1084,6 @@ pub(crate) struct CfgSelectNoMatches {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("unreachable predicate")]
|
||||
pub(crate) struct CfgSelectUnreachable {
|
||||
#[primary_span]
|
||||
#[label("this predicate is never reached")]
|
||||
pub span: Span,
|
||||
|
||||
#[label("always matches")]
|
||||
pub wildcard_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`#[eii_declaration(...)]` is only valid on macros")]
|
||||
pub(crate) struct EiiExternTargetExpectedMacro {
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ fn make_format_args(
|
|||
ecx: &mut ExtCtxt<'_>,
|
||||
input: MacroInput,
|
||||
append_newline: bool,
|
||||
macro_span: Span,
|
||||
) -> ExpandResult<Result<FormatArgs, ErrorGuaranteed>, ()> {
|
||||
let msg = "format argument must be a string literal";
|
||||
let unexpanded_fmt_span = input.fmtstr.span;
|
||||
|
|
@ -333,6 +334,23 @@ fn make_format_args(
|
|||
let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end));
|
||||
e.sugg_ = Some(errors::InvalidFormatStringSuggestion::AddMissingColon { span });
|
||||
}
|
||||
parse::Suggestion::UseRustDebugPrintingMacro => {
|
||||
// This targets `println!("{=}", x);` and `println!("{0=}", x);`
|
||||
if let [arg] = args.all_args() {
|
||||
let expr_span = arg.expr.span;
|
||||
if let Ok(expr_snippet) = ecx.source_map().span_to_snippet(expr_span) {
|
||||
let replacement = format!("{}!({})", "dbg", expr_snippet);
|
||||
|
||||
let call_span = macro_span.source_callsite();
|
||||
e.sugg_ = Some(
|
||||
errors::InvalidFormatStringSuggestion::UseRustDebugPrintingMacro {
|
||||
macro_span: call_span,
|
||||
replacement,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let guar = ecx.dcx().emit_err(e);
|
||||
return ExpandResult::Ready(Err(guar));
|
||||
|
|
@ -1048,7 +1066,7 @@ fn expand_format_args_impl<'cx>(
|
|||
sp = ecx.with_def_site_ctxt(sp);
|
||||
ExpandResult::Ready(match parse_args(ecx, sp, tts) {
|
||||
Ok(input) => {
|
||||
let ExpandResult::Ready(mac) = make_format_args(ecx, input, nl) else {
|
||||
let ExpandResult::Ready(mac) = make_format_args(ecx, input, nl, sp) else {
|
||||
return ExpandResult::Retry(());
|
||||
};
|
||||
match mac {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ pub(crate) fn expand(
|
|||
|
||||
// Generate anonymous constant serving as container for the allocator methods.
|
||||
let const_ty = ecx.ty(ty_span, TyKind::Tup(ThinVec::new()));
|
||||
let const_body = ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts)));
|
||||
let const_body = ast::ConstItemRhsKind::new_body(ecx.expr_block(ecx.block(span, stmts)));
|
||||
let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
|
||||
let const_item = if is_stmt {
|
||||
Annotatable::Stmt(Box::new(ecx.stmt_item(span, const_item)))
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![feature(assert_matches)]
|
||||
#![cfg_attr(bootstrap, feature(assert_matches))]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> Box<ast::Item> {
|
|||
cx.attr_nested_word(sym::allow, sym::deprecated, span),
|
||||
]);
|
||||
|
||||
let block = ast::ConstItemRhs::Body(cx.expr_block(
|
||||
let block = ast::ConstItemRhsKind::new_body(cx.expr_block(
|
||||
cx.block(span, thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]),
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ pub(crate) fn expand_test_or_bench(
|
|||
ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
|
||||
define_opaque: None,
|
||||
// test::TestDescAndFn {
|
||||
rhs: Some(ast::ConstItemRhs::Body(
|
||||
rhs_kind: ast::ConstItemRhsKind::new_body(
|
||||
cx.expr_struct(
|
||||
sp,
|
||||
test_path("TestDescAndFn"),
|
||||
|
|
@ -371,7 +371,7 @@ pub(crate) fn expand_test_or_bench(
|
|||
field("testfn", test_fn), // }
|
||||
],
|
||||
), // }
|
||||
)),
|
||||
),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ use rustc_ast::entry::EntryPointType;
|
|||
use rustc_ast::mut_visit::*;
|
||||
use rustc_ast::visit::Visitor;
|
||||
use rustc_ast::{ModKind, attr};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_attr_parsing::AttributeParser;
|
||||
use rustc_expand::base::{ExtCtxt, ResolverExpand};
|
||||
use rustc_expand::expand::{AstFragment, ExpansionConfig};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS;
|
||||
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
|
||||
|
|
@ -60,7 +61,7 @@ pub fn inject(
|
|||
|
||||
// Do this here so that the test_runner crate attribute gets marked as used
|
||||
// even in non-test builds
|
||||
let test_runner = get_test_runner(dcx, krate);
|
||||
let test_runner = get_test_runner(sess, features, krate);
|
||||
|
||||
if sess.is_test_crate() {
|
||||
let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) {
|
||||
|
|
@ -386,20 +387,16 @@ fn get_test_name(i: &ast::Item) -> Option<Symbol> {
|
|||
attr::first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker)
|
||||
}
|
||||
|
||||
fn get_test_runner(dcx: DiagCtxtHandle<'_>, krate: &ast::Crate) -> Option<ast::Path> {
|
||||
let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
|
||||
let meta_list = test_attr.meta_item_list()?;
|
||||
let span = test_attr.span;
|
||||
match &*meta_list {
|
||||
[single] => match single.meta_item() {
|
||||
Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
|
||||
_ => {
|
||||
dcx.emit_err(errors::TestRunnerInvalid { span });
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
dcx.emit_err(errors::TestRunnerNargs { span });
|
||||
}
|
||||
fn get_test_runner(sess: &Session, features: &Features, krate: &ast::Crate) -> Option<ast::Path> {
|
||||
match AttributeParser::parse_limited(
|
||||
sess,
|
||||
&krate.attrs,
|
||||
sym::test_runner,
|
||||
krate.spans.inner_span,
|
||||
krate.id,
|
||||
Some(features),
|
||||
) {
|
||||
Some(rustc_hir::Attribute::Parsed(AttributeKind::TestRunner(path))) => Some(path),
|
||||
_ => None,
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,29 +2,26 @@
|
|||
//! standalone executable.
|
||||
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
use cranelift_object::{ObjectBuilder, ObjectModule};
|
||||
use rustc_codegen_ssa::assert_module_sources::CguReuse;
|
||||
use rustc_codegen_ssa::back::link::ensure_removed;
|
||||
use rustc_codegen_ssa::back::write::{CompiledModules, produce_final_output_artifacts};
|
||||
use rustc_codegen_ssa::base::determine_cgu_reuse;
|
||||
use rustc_codegen_ssa::{
|
||||
CodegenResults, CompiledModule, CrateInfo, ModuleKind, errors as ssa_errors,
|
||||
};
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind};
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
|
||||
use rustc_hir::attrs::Linkage as RLinkage;
|
||||
use rustc_metadata::fs::copy_to_stdout;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::mono::{CodegenUnit, MonoItem, MonoItemData, Visibility};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{OutFileName, OutputFilenames, OutputType};
|
||||
use rustc_session::config::{OutputFilenames, OutputType};
|
||||
|
||||
use crate::base::CodegenedFunction;
|
||||
use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken};
|
||||
|
|
@ -125,203 +122,22 @@ impl OngoingCodegen {
|
|||
|
||||
sess.dcx().abort_if_errors();
|
||||
|
||||
let codegen_results = CodegenResults {
|
||||
modules,
|
||||
allocator_module: self.allocator_module,
|
||||
crate_info: self.crate_info,
|
||||
};
|
||||
let compiled_modules = CompiledModules { modules, allocator_module: self.allocator_module };
|
||||
|
||||
produce_final_output_artifacts(sess, &codegen_results, outputs);
|
||||
produce_final_output_artifacts(sess, &compiled_modules, outputs);
|
||||
|
||||
(codegen_results, work_products)
|
||||
(
|
||||
CodegenResults {
|
||||
crate_info: self.crate_info,
|
||||
|
||||
modules: compiled_modules.modules,
|
||||
allocator_module: compiled_modules.allocator_module,
|
||||
},
|
||||
work_products,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Adapted from https://github.com/rust-lang/rust/blob/73476d49904751f8d90ce904e16dfbc278083d2c/compiler/rustc_codegen_ssa/src/back/write.rs#L547C1-L706C2
|
||||
fn produce_final_output_artifacts(
|
||||
sess: &Session,
|
||||
codegen_results: &CodegenResults,
|
||||
crate_output: &OutputFilenames,
|
||||
) {
|
||||
let user_wants_bitcode = false;
|
||||
let mut user_wants_objects = false;
|
||||
|
||||
// Produce final compile outputs.
|
||||
let copy_gracefully = |from: &Path, to: &OutFileName| match to {
|
||||
OutFileName::Stdout => {
|
||||
if let Err(e) = copy_to_stdout(from) {
|
||||
sess.dcx().emit_err(ssa_errors::CopyPath::new(from, to.as_path(), e));
|
||||
}
|
||||
}
|
||||
OutFileName::Real(path) => {
|
||||
if let Err(e) = fs::copy(from, path) {
|
||||
sess.dcx().emit_err(ssa_errors::CopyPath::new(from, path, e));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| {
|
||||
if codegen_results.modules.len() == 1 {
|
||||
// 1) Only one codegen unit. In this case it's no difficulty
|
||||
// to copy `foo.0.x` to `foo.x`.
|
||||
let path = crate_output.temp_path_for_cgu(
|
||||
output_type,
|
||||
&codegen_results.modules[0].name,
|
||||
sess.invocation_temp.as_deref(),
|
||||
);
|
||||
let output = crate_output.path(output_type);
|
||||
if !output_type.is_text_output() && output.is_tty() {
|
||||
sess.dcx()
|
||||
.emit_err(ssa_errors::BinaryOutputToTty { shorthand: output_type.shorthand() });
|
||||
} else {
|
||||
copy_gracefully(&path, &output);
|
||||
}
|
||||
if !sess.opts.cg.save_temps && !keep_numbered {
|
||||
// The user just wants `foo.x`, not `foo.#module-name#.x`.
|
||||
ensure_removed(sess.dcx(), &path);
|
||||
}
|
||||
} else {
|
||||
if crate_output.outputs.contains_explicit_name(&output_type) {
|
||||
// 2) Multiple codegen units, with `--emit foo=some_name`. We have
|
||||
// no good solution for this case, so warn the user.
|
||||
sess.dcx()
|
||||
.emit_warn(ssa_errors::IgnoringEmitPath { extension: output_type.extension() });
|
||||
} else if crate_output.single_output_file.is_some() {
|
||||
// 3) Multiple codegen units, with `-o some_name`. We have
|
||||
// no good solution for this case, so warn the user.
|
||||
sess.dcx()
|
||||
.emit_warn(ssa_errors::IgnoringOutput { extension: output_type.extension() });
|
||||
} else {
|
||||
// 4) Multiple codegen units, but no explicit name. We
|
||||
// just leave the `foo.0.x` files in place.
|
||||
// (We don't have to do any work in this case.)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Flag to indicate whether the user explicitly requested bitcode.
|
||||
// Otherwise, we produced it only as a temporary output, and will need
|
||||
// to get rid of it.
|
||||
for output_type in crate_output.outputs.keys() {
|
||||
match *output_type {
|
||||
OutputType::Bitcode | OutputType::ThinLinkBitcode => {
|
||||
// Cranelift doesn't have bitcode
|
||||
// user_wants_bitcode = true;
|
||||
// // Copy to .bc, but always keep the .0.bc. There is a later
|
||||
// // check to figure out if we should delete .0.bc files, or keep
|
||||
// // them for making an rlib.
|
||||
// copy_if_one_unit(OutputType::Bitcode, true);
|
||||
}
|
||||
OutputType::LlvmAssembly => {
|
||||
// Cranelift IR text already emitted during codegen
|
||||
// copy_if_one_unit(OutputType::LlvmAssembly, false);
|
||||
}
|
||||
OutputType::Assembly => {
|
||||
// Currently no support for emitting raw assembly files
|
||||
// copy_if_one_unit(OutputType::Assembly, false);
|
||||
}
|
||||
OutputType::Object => {
|
||||
user_wants_objects = true;
|
||||
copy_if_one_unit(OutputType::Object, true);
|
||||
}
|
||||
OutputType::Mir | OutputType::Metadata | OutputType::Exe | OutputType::DepInfo => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up unwanted temporary files.
|
||||
|
||||
// We create the following files by default:
|
||||
// - #crate#.#module-name#.bc
|
||||
// - #crate#.#module-name#.o
|
||||
// - #crate#.crate.metadata.bc
|
||||
// - #crate#.crate.metadata.o
|
||||
// - #crate#.o (linked from crate.##.o)
|
||||
// - #crate#.bc (copied from crate.##.bc)
|
||||
// We may create additional files if requested by the user (through
|
||||
// `-C save-temps` or `--emit=` flags).
|
||||
|
||||
if !sess.opts.cg.save_temps {
|
||||
// Remove the temporary .#module-name#.o objects. If the user didn't
|
||||
// explicitly request bitcode (with --emit=bc), and the bitcode is not
|
||||
// needed for building an rlib, then we must remove .#module-name#.bc as
|
||||
// well.
|
||||
|
||||
// Specific rules for keeping .#module-name#.bc:
|
||||
// - If the user requested bitcode (`user_wants_bitcode`), and
|
||||
// codegen_units > 1, then keep it.
|
||||
// - If the user requested bitcode but codegen_units == 1, then we
|
||||
// can toss .#module-name#.bc because we copied it to .bc earlier.
|
||||
// - If we're not building an rlib and the user didn't request
|
||||
// bitcode, then delete .#module-name#.bc.
|
||||
// If you change how this works, also update back::link::link_rlib,
|
||||
// where .#module-name#.bc files are (maybe) deleted after making an
|
||||
// rlib.
|
||||
let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe);
|
||||
|
||||
let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units().as_usize() > 1;
|
||||
|
||||
let keep_numbered_objects =
|
||||
needs_crate_object || (user_wants_objects && sess.codegen_units().as_usize() > 1);
|
||||
|
||||
for module in codegen_results.modules.iter() {
|
||||
if let Some(ref path) = module.object {
|
||||
if !keep_numbered_objects {
|
||||
ensure_removed(sess.dcx(), path);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref path) = module.dwarf_object {
|
||||
if !keep_numbered_objects {
|
||||
ensure_removed(sess.dcx(), path);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref path) = module.bytecode {
|
||||
if !keep_numbered_bitcode {
|
||||
ensure_removed(sess.dcx(), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !user_wants_bitcode {
|
||||
if let Some(ref allocator_module) = codegen_results.allocator_module {
|
||||
if let Some(ref path) = allocator_module.bytecode {
|
||||
ensure_removed(sess.dcx(), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sess.opts.json_artifact_notifications {
|
||||
if codegen_results.modules.len() == 1 {
|
||||
codegen_results.modules[0].for_each_output(|_path, ty| {
|
||||
if sess.opts.output_types.contains_key(&ty) {
|
||||
let descr = ty.shorthand();
|
||||
// for single cgu file is renamed to drop cgu specific suffix
|
||||
// so we regenerate it the same way
|
||||
let path = crate_output.path(ty);
|
||||
sess.dcx().emit_artifact_notification(path.as_path(), descr);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for module in &codegen_results.modules {
|
||||
module.for_each_output(|path, ty| {
|
||||
if sess.opts.output_types.contains_key(&ty) {
|
||||
let descr = ty.shorthand();
|
||||
sess.dcx().emit_artifact_notification(path, descr);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We leave the following files around by default:
|
||||
// - #crate#.o
|
||||
// - #crate#.crate.metadata.o
|
||||
// - #crate#.bc
|
||||
// These are used in linking steps and will be cleaned up afterward.
|
||||
}
|
||||
|
||||
fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> {
|
||||
let isa = crate::build_isa(sess, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ extern crate rustc_hir;
|
|||
extern crate rustc_incremental;
|
||||
extern crate rustc_index;
|
||||
extern crate rustc_log;
|
||||
extern crate rustc_metadata;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
extern crate rustc_symbol_mangling;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter}
|
|||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_errors::{DiagCtxt, DiagCtxtHandle};
|
||||
use rustc_log::tracing::info;
|
||||
use rustc_middle::bug;
|
||||
|
|
@ -50,7 +51,7 @@ struct LtoData {
|
|||
}
|
||||
|
||||
fn prepare_lto(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
) -> LtoData {
|
||||
|
|
@ -111,7 +112,8 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> {
|
|||
/// Performs fat LTO by merging all modules into a single one and returning it
|
||||
/// for further optimization.
|
||||
pub(crate) fn run_fat(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<GccCodegenBackend>>,
|
||||
|
|
@ -123,6 +125,7 @@ pub(crate) fn run_fat(
|
|||
lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();*/
|
||||
fat_lto(
|
||||
cgcx,
|
||||
prof,
|
||||
dcx,
|
||||
modules,
|
||||
lto_data.upstream_modules,
|
||||
|
|
@ -132,14 +135,15 @@ pub(crate) fn run_fat(
|
|||
}
|
||||
|
||||
fn fat_lto(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
_dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<FatLtoInput<GccCodegenBackend>>,
|
||||
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
tmp_path: TempDir,
|
||||
//symbols_below_threshold: &[String],
|
||||
) -> ModuleCodegen<GccContext> {
|
||||
let _timer = cgcx.prof.generic_activity("GCC_fat_lto_build_monolithic_module");
|
||||
let _timer = prof.generic_activity("GCC_fat_lto_build_monolithic_module");
|
||||
info!("going for a fat lto");
|
||||
|
||||
// Sort out all our lists of incoming modules into two lists.
|
||||
|
|
@ -223,8 +227,7 @@ fn fat_lto(
|
|||
// We add the object files and save in should_combine_object_files that we should combine
|
||||
// them into a single object file when compiling later.
|
||||
for (bc_decoded, name) in serialized_modules {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
let _timer = prof
|
||||
.generic_activity_with_arg_recorder("GCC_fat_lto_link_module", |recorder| {
|
||||
recorder.record_arg(format!("{:?}", name))
|
||||
});
|
||||
|
|
@ -283,7 +286,8 @@ impl ModuleBufferMethods for ModuleBuffer {
|
|||
/// lists, one of the modules that need optimization and another for modules that
|
||||
/// can simply be copied over from the incr. comp. cache.
|
||||
pub(crate) fn run_thin(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<(String, ThinBuffer)>,
|
||||
|
|
@ -298,6 +302,7 @@ pub(crate) fn run_thin(
|
|||
}
|
||||
thin_lto(
|
||||
cgcx,
|
||||
prof,
|
||||
dcx,
|
||||
modules,
|
||||
lto_data.upstream_modules,
|
||||
|
|
@ -345,7 +350,8 @@ pub(crate) fn prepare_thin(module: ModuleCodegen<GccContext>) -> (String, ThinBu
|
|||
/// all of the `LtoModuleCodegen` units returned below and destroyed once
|
||||
/// they all go out of scope.
|
||||
fn thin_lto(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
_cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
_dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<(String, ThinBuffer)>,
|
||||
serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
|
|
@ -353,7 +359,7 @@ fn thin_lto(
|
|||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
//_symbols_below_threshold: &[String],
|
||||
) -> (Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>) {
|
||||
let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis");
|
||||
let _timer = prof.generic_activity("LLVM_thin_lto_global_analysis");
|
||||
info!("going for that thin, thin LTO");
|
||||
|
||||
/*let green_modules: FxHashMap<_, _> =
|
||||
|
|
@ -520,11 +526,9 @@ fn thin_lto(
|
|||
|
||||
pub fn optimize_thin_module(
|
||||
thin_module: ThinModule<GccCodegenBackend>,
|
||||
_cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
_cgcx: &CodegenContext,
|
||||
) -> ModuleCodegen<GccContext> {
|
||||
//let module_name = &thin_module.shared.module_names[thin_module.idx];
|
||||
/*let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap());
|
||||
let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&dcx, e))?;*/
|
||||
|
||||
// Right now the implementation we've got only works over serialized
|
||||
// modules, so we create a fresh new LLVM context and parse the module
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use rustc_codegen_ssa::back::write::{
|
|||
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, SharedEmitter,
|
||||
};
|
||||
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_errors::DiagCtxt;
|
||||
use rustc_fs_util::link_or_copy;
|
||||
use rustc_log::tracing::debug;
|
||||
|
|
@ -14,10 +15,11 @@ use rustc_target::spec::SplitDebuginfo;
|
|||
|
||||
use crate::base::add_pic_option;
|
||||
use crate::errors::CopyBitcode;
|
||||
use crate::{GccCodegenBackend, GccContext, LtoMode};
|
||||
use crate::{GccContext, LtoMode};
|
||||
|
||||
pub(crate) fn codegen(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
module: ModuleCodegen<GccContext>,
|
||||
config: &ModuleConfig,
|
||||
|
|
@ -25,7 +27,7 @@ pub(crate) fn codegen(
|
|||
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
|
||||
let dcx = dcx.handle();
|
||||
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name);
|
||||
let _timer = prof.generic_activity_with_arg("GCC_module_codegen", &*module.name);
|
||||
{
|
||||
let context = &module.module_llvm.context;
|
||||
|
||||
|
|
@ -44,9 +46,8 @@ pub(crate) fn codegen(
|
|||
);
|
||||
|
||||
if config.bitcode_needed() {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name);
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name);
|
||||
|
||||
// TODO(antoyo)
|
||||
/*if let Some(bitcode_filename) = bc_out.file_name() {
|
||||
|
|
@ -58,8 +59,7 @@ pub(crate) fn codegen(
|
|||
}*/
|
||||
|
||||
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
let _timer = prof
|
||||
.generic_activity_with_arg("GCC_module_codegen_emit_bitcode", &*module.name);
|
||||
if lto_supported {
|
||||
context.add_command_line_option("-flto=auto");
|
||||
|
|
@ -70,8 +70,7 @@ pub(crate) fn codegen(
|
|||
}
|
||||
|
||||
if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
let _timer = prof
|
||||
.generic_activity_with_arg("GCC_module_codegen_embed_bitcode", &*module.name);
|
||||
if lto_supported {
|
||||
// TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes?
|
||||
|
|
@ -98,7 +97,7 @@ pub(crate) fn codegen(
|
|||
|
||||
if config.emit_asm {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name);
|
||||
prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name);
|
||||
let path = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Assembly,
|
||||
&module.name,
|
||||
|
|
@ -109,9 +108,8 @@ pub(crate) fn codegen(
|
|||
|
||||
match config.emit_obj {
|
||||
EmitObj::ObjectCode(_) => {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("GCC_module_codegen_emit_obj", &*module.name);
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("GCC_module_codegen_emit_obj", &*module.name);
|
||||
if env::var("CG_GCCJIT_DUMP_MODULE_NAMES").as_deref() == Ok("1") {
|
||||
println!("Module {}", module.name);
|
||||
}
|
||||
|
|
@ -227,7 +225,7 @@ pub(crate) fn codegen(
|
|||
}
|
||||
|
||||
pub(crate) fn save_temp_bitcode(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
_module: &ModuleCodegen<GccContext>,
|
||||
_name: &str,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -22,13 +22,18 @@ use rustc_codegen_ssa::traits::{
|
|||
ArgAbiBuilderMethods, BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods,
|
||||
IntrinsicCallBuilderMethods, LayoutTypeCodegenMethods,
|
||||
};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
|
||||
#[cfg(feature = "master")]
|
||||
use rustc_middle::ty::layout::FnAbiOf;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, Instance, Ty};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_target::callconv::{ArgAbi, PassMode};
|
||||
|
||||
use crate::abi::{FnAbiGccExt, GccType};
|
||||
#[cfg(feature = "master")]
|
||||
use crate::abi::FnAbiGccExt;
|
||||
use crate::abi::GccType;
|
||||
use crate::builder::Builder;
|
||||
use crate::common::{SignType, TypeReflection};
|
||||
use crate::context::CodegenCx;
|
||||
|
|
@ -617,8 +622,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
*func
|
||||
} else {
|
||||
self.linkage.set(FunctionType::Extern);
|
||||
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
|
||||
let fn_ty = fn_abi.gcc_type(self);
|
||||
|
||||
let func = match sym {
|
||||
"llvm.fma.f16" => {
|
||||
|
|
@ -631,13 +634,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
|
||||
self.intrinsics.borrow_mut().insert(sym.to_string(), func);
|
||||
|
||||
self.on_stack_function_params
|
||||
.borrow_mut()
|
||||
.insert(func, fn_ty.on_stack_param_indices);
|
||||
#[cfg(feature = "master")]
|
||||
for fn_attr in fn_ty.fn_attributes {
|
||||
func.add_attribute(fn_attr);
|
||||
}
|
||||
self.on_stack_function_params.borrow_mut().insert(func, FxHashSet::default());
|
||||
|
||||
crate::attributes::from_fn_attrs(self, func, instance);
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ use rustc_codegen_ssa::target_features::cfg_target_feature;
|
|||
use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods};
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_data_structures::sync::IntoDynSyncSend;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
|
|
@ -374,7 +375,7 @@ impl ExtraBackendMethods for GccCodegenBackend {
|
|||
_features: &[String],
|
||||
) -> TargetMachineFactoryFn<Self> {
|
||||
// TODO(antoyo): set opt level.
|
||||
Arc::new(|_| Ok(()))
|
||||
Arc::new(|_, _| ())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -421,24 +422,26 @@ unsafe impl Sync for SyncContext {}
|
|||
impl WriteBackendMethods for GccCodegenBackend {
|
||||
type Module = GccContext;
|
||||
type TargetMachine = ();
|
||||
type TargetMachineError = ();
|
||||
type ModuleBuffer = ModuleBuffer;
|
||||
type ThinData = ThinData;
|
||||
type ThinBuffer = ThinBuffer;
|
||||
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
_tm_factory: TargetMachineFactoryFn<Self>,
|
||||
// FIXME(bjorn3): Limit LTO exports to these symbols
|
||||
_exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
) -> ModuleCodegen<Self::Module> {
|
||||
back::lto::run_fat(cgcx, shared_emitter, each_linked_rlib_for_lto, modules)
|
||||
back::lto::run_fat(cgcx, prof, shared_emitter, each_linked_rlib_for_lto, modules)
|
||||
}
|
||||
|
||||
fn run_thin_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
// FIXME(bjorn3): Limit LTO exports to these symbols
|
||||
_exported_symbols_for_lto: &[String],
|
||||
|
|
@ -446,7 +449,7 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
modules: Vec<(String, Self::ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
|
||||
back::lto::run_thin(cgcx, dcx, each_linked_rlib_for_lto, modules, cached_modules)
|
||||
back::lto::run_thin(cgcx, prof, dcx, each_linked_rlib_for_lto, modules, cached_modules)
|
||||
}
|
||||
|
||||
fn print_pass_timings(&self) {
|
||||
|
|
@ -458,7 +461,8 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
}
|
||||
|
||||
fn optimize(
|
||||
_cgcx: &CodegenContext<Self>,
|
||||
_cgcx: &CodegenContext,
|
||||
_prof: &SelfProfilerRef,
|
||||
_shared_emitter: &SharedEmitter,
|
||||
module: &mut ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
|
|
@ -467,20 +471,23 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
}
|
||||
|
||||
fn optimize_thin(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
_prof: &SelfProfilerRef,
|
||||
_shared_emitter: &SharedEmitter,
|
||||
_tm_factory: TargetMachineFactoryFn<Self>,
|
||||
thin: ThinModule<Self>,
|
||||
) -> ModuleCodegen<Self::Module> {
|
||||
back::lto::optimize_thin_module(thin, cgcx)
|
||||
}
|
||||
|
||||
fn codegen(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
module: ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
) -> CompiledModule {
|
||||
back::write::codegen(cgcx, shared_emitter, module, config)
|
||||
back::write::codegen(cgcx, prof, shared_emitter, module, config)
|
||||
}
|
||||
|
||||
fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
|
||||
|
|
|
|||
|
|
@ -301,17 +301,6 @@ fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll A
|
|||
Some(sspattr.create_attr(cx.llcx))
|
||||
}
|
||||
|
||||
fn backchain_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
|
||||
if sess.target.arch != Arch::S390x {
|
||||
return None;
|
||||
}
|
||||
|
||||
let requested_features = sess.opts.cg.target_feature.split(',');
|
||||
let found_positive = requested_features.clone().any(|r| r == "+backchain");
|
||||
|
||||
if found_positive { Some(llvm::CreateAttrString(cx.llcx, "backchain")) } else { None }
|
||||
}
|
||||
|
||||
pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute {
|
||||
let target_cpu = llvm_util::target_cpu(sess);
|
||||
llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu)
|
||||
|
|
@ -530,9 +519,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
if let Some(align) = codegen_fn_attrs.alignment {
|
||||
llvm::set_alignment(llfn, align);
|
||||
}
|
||||
if let Some(backchain) = backchain_attr(cx, sess) {
|
||||
to_add.push(backchain);
|
||||
}
|
||||
to_add.extend(patchable_function_entry_attrs(
|
||||
cx,
|
||||
sess,
|
||||
|
|
|
|||
|
|
@ -9,11 +9,14 @@ use std::{io, iter, slice};
|
|||
use object::read::archive::ArchiveFile;
|
||||
use object::{Object, ObjectSection};
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter};
|
||||
use rustc_codegen_ssa::back::write::{
|
||||
CodegenContext, FatLtoInput, SharedEmitter, TargetMachineFactoryFn,
|
||||
};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_errors::{DiagCtxt, DiagCtxtHandle};
|
||||
use rustc_hir::attrs::SanitizerSet;
|
||||
use rustc_middle::bug;
|
||||
|
|
@ -33,7 +36,7 @@ use crate::{LlvmCodegenBackend, ModuleLlvm};
|
|||
const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin";
|
||||
|
||||
fn prepare_lto(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
|
|
@ -123,7 +126,7 @@ fn prepare_lto(
|
|||
|
||||
fn get_bitcode_slice_from_object_data<'a>(
|
||||
obj: &'a [u8],
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
) -> Result<&'a [u8], LtoBitcodeFromRlib> {
|
||||
// We're about to assume the data here is an object file with sections, but if it's raw LLVM IR
|
||||
// that won't work. Fortunately, if that's what we have we can just return the object directly,
|
||||
|
|
@ -149,8 +152,10 @@ fn get_bitcode_slice_from_object_data<'a>(
|
|||
/// Performs fat LTO by merging all modules into a single one and returning it
|
||||
/// for further optimization.
|
||||
pub(crate) fn run_fat(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<LlvmCodegenBackend>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
|
||||
|
|
@ -161,14 +166,24 @@ pub(crate) fn run_fat(
|
|||
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx);
|
||||
let symbols_below_threshold =
|
||||
symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
|
||||
fat_lto(cgcx, dcx, shared_emitter, modules, upstream_modules, &symbols_below_threshold)
|
||||
fat_lto(
|
||||
cgcx,
|
||||
prof,
|
||||
dcx,
|
||||
shared_emitter,
|
||||
tm_factory,
|
||||
modules,
|
||||
upstream_modules,
|
||||
&symbols_below_threshold,
|
||||
)
|
||||
}
|
||||
|
||||
/// Performs thin LTO by performing necessary global analysis and returning two
|
||||
/// lists, one of the modules that need optimization and another for modules that
|
||||
/// can simply be copied over from the incr. comp. cache.
|
||||
pub(crate) fn run_thin(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
|
|
@ -185,7 +200,7 @@ pub(crate) fn run_thin(
|
|||
is deferred to the linker"
|
||||
);
|
||||
}
|
||||
thin_lto(cgcx, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold)
|
||||
thin_lto(cgcx, prof, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold)
|
||||
}
|
||||
|
||||
pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBuffer) {
|
||||
|
|
@ -195,14 +210,16 @@ pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBu
|
|||
}
|
||||
|
||||
fn fat_lto(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
shared_emitter: &SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<LlvmCodegenBackend>,
|
||||
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
|
||||
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
symbols_below_threshold: &[*const libc::c_char],
|
||||
) -> ModuleCodegen<ModuleLlvm> {
|
||||
let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module");
|
||||
let _timer = prof.generic_activity("LLVM_fat_lto_build_monolithic_module");
|
||||
info!("going for a fat lto");
|
||||
|
||||
// Sort out all our lists of incoming modules into two lists.
|
||||
|
|
@ -252,7 +269,7 @@ fn fat_lto(
|
|||
assert!(!serialized_modules.is_empty(), "must have at least one serialized module");
|
||||
let (buffer, name) = serialized_modules.remove(0);
|
||||
info!("no in-memory regular modules to choose from, parsing {:?}", name);
|
||||
let llvm_module = ModuleLlvm::parse(cgcx, &name, buffer.data(), dcx);
|
||||
let llvm_module = ModuleLlvm::parse(cgcx, tm_factory, &name, buffer.data(), dcx);
|
||||
ModuleCodegen::new_regular(name.into_string().unwrap(), llvm_module)
|
||||
}
|
||||
};
|
||||
|
|
@ -291,8 +308,7 @@ fn fat_lto(
|
|||
// above, this is all mostly handled in C++.
|
||||
let mut linker = Linker::new(llmod);
|
||||
for (bc_decoded, name) in serialized_modules {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
let _timer = prof
|
||||
.generic_activity_with_arg_recorder("LLVM_fat_lto_link_module", |recorder| {
|
||||
recorder.record_arg(format!("{name:?}"))
|
||||
});
|
||||
|
|
@ -381,14 +397,15 @@ impl Drop for Linker<'_> {
|
|||
/// all of the `LtoModuleCodegen` units returned below and destroyed once
|
||||
/// they all go out of scope.
|
||||
fn thin_lto(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<(String, ThinBuffer)>,
|
||||
serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
symbols_below_threshold: &[*const libc::c_char],
|
||||
) -> (Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>) {
|
||||
let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis");
|
||||
let _timer = prof.generic_activity("LLVM_thin_lto_global_analysis");
|
||||
unsafe {
|
||||
info!("going for that thin, thin LTO");
|
||||
|
||||
|
|
@ -585,12 +602,13 @@ pub(crate) fn enable_autodiff_settings(ad: &[config::AutoDiff]) {
|
|||
}
|
||||
|
||||
pub(crate) fn run_pass_manager(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
module: &mut ModuleCodegen<ModuleLlvm>,
|
||||
thin: bool,
|
||||
) {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
|
||||
let _timer = prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
|
||||
let config = &cgcx.module_config;
|
||||
|
||||
// Now we have one massive module inside of llmod. Time to run the
|
||||
|
|
@ -616,7 +634,7 @@ pub(crate) fn run_pass_manager(
|
|||
};
|
||||
|
||||
unsafe {
|
||||
write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage);
|
||||
write::llvm_optimize(cgcx, prof, dcx, module, None, config, opt_level, opt_stage, stage);
|
||||
}
|
||||
|
||||
if cfg!(feature = "llvm_enzyme") && enable_ad && !thin {
|
||||
|
|
@ -624,7 +642,9 @@ pub(crate) fn run_pass_manager(
|
|||
let stage = write::AutodiffStage::PostAD;
|
||||
if !config.autodiff.contains(&config::AutoDiff::NoPostopt) {
|
||||
unsafe {
|
||||
write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage);
|
||||
write::llvm_optimize(
|
||||
cgcx, prof, dcx, module, None, config, opt_level, opt_stage, stage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -726,8 +746,10 @@ impl Drop for ThinBuffer {
|
|||
}
|
||||
|
||||
pub(crate) fn optimize_thin_module(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<LlvmCodegenBackend>,
|
||||
thin_module: ThinModule<LlvmCodegenBackend>,
|
||||
) -> ModuleCodegen<ModuleLlvm> {
|
||||
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
|
||||
|
|
@ -740,7 +762,7 @@ pub(crate) fn optimize_thin_module(
|
|||
// into that context. One day, however, we may do this for upstream
|
||||
// crates but for locally codegened modules we may be able to reuse
|
||||
// that LLVM Context and Module.
|
||||
let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx);
|
||||
let module_llvm = ModuleLlvm::parse(cgcx, tm_factory, module_name, thin_module.data(), dcx);
|
||||
let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm);
|
||||
// Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here.
|
||||
if cgcx.module_config.embed_bitcode() {
|
||||
|
|
@ -760,8 +782,7 @@ pub(crate) fn optimize_thin_module(
|
|||
// You can find some more comments about these functions in the LLVM
|
||||
// bindings we've got (currently `PassWrapper.cpp`)
|
||||
{
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
|
||||
let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
|
||||
unsafe {
|
||||
llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target.raw())
|
||||
};
|
||||
|
|
@ -769,9 +790,8 @@ pub(crate) fn optimize_thin_module(
|
|||
}
|
||||
|
||||
{
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
|
||||
if unsafe { !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) }
|
||||
{
|
||||
write::llvm_err(dcx, LlvmError::PrepareThinLtoModule);
|
||||
|
|
@ -780,9 +800,8 @@ pub(crate) fn optimize_thin_module(
|
|||
}
|
||||
|
||||
{
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
|
||||
if unsafe { !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) }
|
||||
{
|
||||
write::llvm_err(dcx, LlvmError::PrepareThinLtoModule);
|
||||
|
|
@ -791,8 +810,7 @@ pub(crate) fn optimize_thin_module(
|
|||
}
|
||||
|
||||
{
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
|
||||
let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
|
||||
if unsafe {
|
||||
!llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target.raw())
|
||||
} {
|
||||
|
|
@ -808,7 +826,7 @@ pub(crate) fn optimize_thin_module(
|
|||
// little differently.
|
||||
{
|
||||
info!("running thin lto passes over {}", module.name);
|
||||
run_pass_manager(cgcx, dcx, &mut module, true);
|
||||
run_pass_manager(cgcx, prof, dcx, &mut module, true);
|
||||
save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ use crate::builder::SBuilder;
|
|||
use crate::builder::gpu_offload::scalar_width;
|
||||
use crate::common::AsCCharPtr;
|
||||
use crate::errors::{
|
||||
CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, UnknownCompression,
|
||||
WithLlvmError, WriteBytecode,
|
||||
CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, ParseTargetMachineConfig,
|
||||
UnknownCompression, WithLlvmError, WriteBytecode,
|
||||
};
|
||||
use crate::llvm::diagnostic::OptimizationDiagnosticKind::*;
|
||||
use crate::llvm::{self, DiagnosticInfo};
|
||||
|
|
@ -111,8 +111,7 @@ pub(crate) fn create_informational_target_machine(
|
|||
// Can't use query system here quite yet because this function is invoked before the query
|
||||
// system/tcx is set up.
|
||||
let features = llvm_util::global_llvm_features(sess, only_base_features);
|
||||
target_machine_factory(sess, config::OptLevel::No, &features)(config)
|
||||
.unwrap_or_else(|err| llvm_err(sess.dcx(), err))
|
||||
target_machine_factory(sess, config::OptLevel::No, &features)(sess.dcx(), config)
|
||||
}
|
||||
|
||||
pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTargetMachine {
|
||||
|
|
@ -138,8 +137,7 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar
|
|||
tcx.sess,
|
||||
tcx.backend_optimization_level(()),
|
||||
tcx.global_backend_features(()),
|
||||
)(config)
|
||||
.unwrap_or_else(|err| llvm_err(tcx.dcx(), err))
|
||||
)(tcx.dcx(), config)
|
||||
}
|
||||
|
||||
fn to_llvm_opt_settings(cfg: config::OptLevel) -> (llvm::CodeGenOptLevel, llvm::CodeGenOptSize) {
|
||||
|
|
@ -278,7 +276,7 @@ pub(crate) fn target_machine_factory(
|
|||
let large_data_threshold = sess.opts.unstable_opts.large_data_threshold.unwrap_or(0);
|
||||
|
||||
let prof = SelfProfilerRef::clone(&sess.prof);
|
||||
Arc::new(move |config: TargetMachineFactoryConfig| {
|
||||
Arc::new(move |dcx: DiagCtxtHandle<'_>, config: TargetMachineFactoryConfig| {
|
||||
// Self-profile timer for invoking a factory to create a target machine.
|
||||
let _prof_timer = prof.generic_activity("target_machine_factory_inner");
|
||||
|
||||
|
|
@ -320,11 +318,12 @@ pub(crate) fn target_machine_factory(
|
|||
use_wasm_eh,
|
||||
large_data_threshold,
|
||||
)
|
||||
.unwrap_or_else(|err| dcx.emit_fatal(ParseTargetMachineConfig(err)))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn save_temp_bitcode(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
module: &ModuleCodegen<ModuleLlvm>,
|
||||
name: &str,
|
||||
) {
|
||||
|
|
@ -359,14 +358,14 @@ pub(crate) enum CodegenDiagnosticsStage {
|
|||
}
|
||||
|
||||
pub(crate) struct DiagnosticHandlers<'a> {
|
||||
data: *mut (&'a CodegenContext<LlvmCodegenBackend>, &'a SharedEmitter),
|
||||
data: *mut (&'a CodegenContext, &'a SharedEmitter),
|
||||
llcx: &'a llvm::Context,
|
||||
old_handler: Option<&'a llvm::DiagnosticHandler>,
|
||||
}
|
||||
|
||||
impl<'a> DiagnosticHandlers<'a> {
|
||||
pub(crate) fn new(
|
||||
cgcx: &'a CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &'a CodegenContext,
|
||||
shared_emitter: &'a SharedEmitter,
|
||||
llcx: &'a llvm::Context,
|
||||
module: &ModuleCodegen<ModuleLlvm>,
|
||||
|
|
@ -432,7 +431,7 @@ impl<'a> Drop for DiagnosticHandlers<'a> {
|
|||
}
|
||||
|
||||
fn report_inline_asm(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
msg: String,
|
||||
level: llvm::DiagnosticLevel,
|
||||
cookie: u64,
|
||||
|
|
@ -464,8 +463,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
|
|||
if user.is_null() {
|
||||
return;
|
||||
}
|
||||
let (cgcx, shared_emitter) =
|
||||
unsafe { *(user as *const (&CodegenContext<LlvmCodegenBackend>, &SharedEmitter)) };
|
||||
let (cgcx, shared_emitter) = unsafe { *(user as *const (&CodegenContext, &SharedEmitter)) };
|
||||
|
||||
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
|
||||
let dcx = dcx.handle();
|
||||
|
|
@ -561,7 +559,8 @@ pub(crate) enum AutodiffStage {
|
|||
}
|
||||
|
||||
pub(crate) unsafe fn llvm_optimize(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
module: &ModuleCodegen<ModuleLlvm>,
|
||||
thin_lto_buffer: Option<&mut *mut llvm::ThinLTOBuffer>,
|
||||
|
|
@ -758,10 +757,9 @@ pub(crate) unsafe fn llvm_optimize(
|
|||
}
|
||||
}
|
||||
|
||||
let mut llvm_profiler = cgcx
|
||||
.prof
|
||||
let mut llvm_profiler = prof
|
||||
.llvm_recording_enabled()
|
||||
.then(|| LlvmSelfProfiler::new(cgcx.prof.get_self_profiler().unwrap()));
|
||||
.then(|| LlvmSelfProfiler::new(prof.get_self_profiler().unwrap()));
|
||||
|
||||
let llvm_selfprofiler =
|
||||
llvm_profiler.as_mut().map(|s| s as *mut _ as *mut c_void).unwrap_or(std::ptr::null_mut());
|
||||
|
|
@ -880,7 +878,7 @@ pub(crate) unsafe fn llvm_optimize(
|
|||
&out_obj,
|
||||
None,
|
||||
llvm::FileType::ObjectFile,
|
||||
&cgcx.prof,
|
||||
prof,
|
||||
true,
|
||||
);
|
||||
// We ignore cgcx.save_temps here and unconditionally always keep our `host.out` artifact.
|
||||
|
|
@ -893,12 +891,13 @@ pub(crate) unsafe fn llvm_optimize(
|
|||
|
||||
// Unsafe due to LLVM calls.
|
||||
pub(crate) fn optimize(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
module: &mut ModuleCodegen<ModuleLlvm>,
|
||||
config: &ModuleConfig,
|
||||
) {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name);
|
||||
let _timer = prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name);
|
||||
|
||||
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
|
||||
let dcx = dcx.handle();
|
||||
|
|
@ -945,6 +944,7 @@ pub(crate) fn optimize(
|
|||
unsafe {
|
||||
llvm_optimize(
|
||||
cgcx,
|
||||
prof,
|
||||
dcx,
|
||||
module,
|
||||
thin_lto_buffer.as_mut(),
|
||||
|
|
@ -966,12 +966,12 @@ pub(crate) fn optimize(
|
|||
&& let Some(thin_link_bitcode_filename) = bc_summary_out.file_name()
|
||||
{
|
||||
let summary_data = thin_lto_buffer.thin_link_data();
|
||||
cgcx.prof.artifact_size(
|
||||
prof.artifact_size(
|
||||
"llvm_bitcode_summary",
|
||||
thin_link_bitcode_filename.to_string_lossy(),
|
||||
summary_data.len() as u64,
|
||||
);
|
||||
let _timer = cgcx.prof.generic_activity_with_arg(
|
||||
let _timer = prof.generic_activity_with_arg(
|
||||
"LLVM_module_codegen_emit_bitcode_summary",
|
||||
&*module.name,
|
||||
);
|
||||
|
|
@ -984,12 +984,13 @@ pub(crate) fn optimize(
|
|||
}
|
||||
|
||||
pub(crate) fn codegen(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
module: ModuleCodegen<ModuleLlvm>,
|
||||
config: &ModuleConfig,
|
||||
) -> CompiledModule {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name);
|
||||
let _timer = prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name);
|
||||
|
||||
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
|
||||
let dcx = dcx.handle();
|
||||
|
|
@ -1028,18 +1029,17 @@ pub(crate) fn codegen(
|
|||
if config.bitcode_needed() {
|
||||
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
|
||||
let thin = {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg(
|
||||
let _timer = prof.generic_activity_with_arg(
|
||||
"LLVM_module_codegen_make_bitcode",
|
||||
&*module.name,
|
||||
);
|
||||
ThinBuffer::new(llmod, config.emit_thin_lto)
|
||||
};
|
||||
let data = thin.data();
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
let _timer = prof
|
||||
.generic_activity_with_arg("LLVM_module_codegen_emit_bitcode", &*module.name);
|
||||
if let Some(bitcode_filename) = bc_out.file_name() {
|
||||
cgcx.prof.artifact_size(
|
||||
prof.artifact_size(
|
||||
"llvm_bitcode",
|
||||
bitcode_filename.to_string_lossy(),
|
||||
data.len() as u64,
|
||||
|
|
@ -1051,8 +1051,7 @@ pub(crate) fn codegen(
|
|||
}
|
||||
|
||||
if config.embed_bitcode() && module.kind == ModuleKind::Regular {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
let _timer = prof
|
||||
.generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name);
|
||||
let thin_bc =
|
||||
module.thin_lto_buffer.as_deref().expect("cannot find embedded bitcode");
|
||||
|
|
@ -1062,7 +1061,7 @@ pub(crate) fn codegen(
|
|||
|
||||
if config.emit_ir {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name);
|
||||
prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name);
|
||||
let out = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::LlvmAssembly,
|
||||
&module.name,
|
||||
|
|
@ -1100,7 +1099,7 @@ pub(crate) fn codegen(
|
|||
unsafe { llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback) };
|
||||
|
||||
if result == llvm::LLVMRustResult::Success {
|
||||
record_artifact_size(&cgcx.prof, "llvm_ir", &out);
|
||||
record_artifact_size(prof, "llvm_ir", &out);
|
||||
}
|
||||
|
||||
result
|
||||
|
|
@ -1110,7 +1109,7 @@ pub(crate) fn codegen(
|
|||
|
||||
if config.emit_asm {
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name);
|
||||
prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name);
|
||||
let path = cgcx.output_filenames.temp_path_for_cgu(
|
||||
OutputType::Assembly,
|
||||
&module.name,
|
||||
|
|
@ -1134,16 +1133,15 @@ pub(crate) fn codegen(
|
|||
&path,
|
||||
None,
|
||||
llvm::FileType::AssemblyFile,
|
||||
&cgcx.prof,
|
||||
prof,
|
||||
config.verify_llvm_ir,
|
||||
);
|
||||
}
|
||||
|
||||
match config.emit_obj {
|
||||
EmitObj::ObjectCode(_) => {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name);
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name);
|
||||
|
||||
let dwo_out = cgcx
|
||||
.output_filenames
|
||||
|
|
@ -1170,7 +1168,7 @@ pub(crate) fn codegen(
|
|||
&obj_out,
|
||||
dwo_out,
|
||||
llvm::FileType::ObjectFile,
|
||||
&cgcx.prof,
|
||||
prof,
|
||||
config.verify_llvm_ir,
|
||||
);
|
||||
}
|
||||
|
|
@ -1190,7 +1188,7 @@ pub(crate) fn codegen(
|
|||
EmitObj::None => {}
|
||||
}
|
||||
|
||||
record_llvm_cgu_instructions_stats(&cgcx.prof, &module.name, llmod);
|
||||
record_llvm_cgu_instructions_stats(prof, &module.name, llmod);
|
||||
}
|
||||
|
||||
// `.dwo` files are only emitted if:
|
||||
|
|
@ -1239,7 +1237,7 @@ fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data:
|
|||
asm
|
||||
}
|
||||
|
||||
pub(crate) fn bitcode_section_name(cgcx: &CodegenContext<LlvmCodegenBackend>) -> &'static CStr {
|
||||
pub(crate) fn bitcode_section_name(cgcx: &CodegenContext) -> &'static CStr {
|
||||
if cgcx.target_is_like_darwin {
|
||||
c"__LLVM,__bitcode"
|
||||
} else if cgcx.target_is_like_aix {
|
||||
|
|
@ -1251,7 +1249,7 @@ pub(crate) fn bitcode_section_name(cgcx: &CodegenContext<LlvmCodegenBackend>) ->
|
|||
|
||||
/// Embed the bitcode of an LLVM module for LTO in the LLVM module itself.
|
||||
fn embed_bitcode(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
llcx: &llvm::Context,
|
||||
llmod: &llvm::Module,
|
||||
bitcode: &[u8],
|
||||
|
|
@ -1335,11 +1333,7 @@ fn embed_bitcode(
|
|||
// when using MSVC linker. We do this only for data, as linker can fix up
|
||||
// code references on its own.
|
||||
// See #26591, #27438
|
||||
fn create_msvc_imps(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
llcx: &llvm::Context,
|
||||
llmod: &llvm::Module,
|
||||
) {
|
||||
fn create_msvc_imps(cgcx: &CodegenContext, llcx: &llvm::Context, llmod: &llvm::Module) {
|
||||
if !cgcx.msvc_imps_needed {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
use std::ffi::CString;
|
||||
|
||||
use bitflags::Flags;
|
||||
use llvm::Linkage::*;
|
||||
use rustc_abi::Align;
|
||||
use rustc_codegen_ssa::common::TypeKind;
|
||||
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
|
||||
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::offload_meta::OffloadMetadata;
|
||||
use rustc_middle::ty::offload_meta::{MappingFlags, OffloadMetadata};
|
||||
|
||||
use crate::builder::Builder;
|
||||
use crate::common::CodegenCx;
|
||||
|
|
@ -28,10 +29,6 @@ pub(crate) struct OffloadGlobals<'ll> {
|
|||
pub mapper_fn_ty: &'ll llvm::Type,
|
||||
|
||||
pub ident_t_global: &'ll llvm::Value,
|
||||
|
||||
// FIXME(offload): Drop this, once we fully automated our offload compilation pipeline, since
|
||||
// LLVM will initialize them for us if it sees gpu kernels being registered.
|
||||
pub init_rtls: &'ll llvm::Value,
|
||||
}
|
||||
|
||||
impl<'ll> OffloadGlobals<'ll> {
|
||||
|
|
@ -42,9 +39,6 @@ impl<'ll> OffloadGlobals<'ll> {
|
|||
let (begin_mapper, _, end_mapper, mapper_fn_ty) = gen_tgt_data_mappers(cx);
|
||||
let ident_t_global = generate_at_one(cx);
|
||||
|
||||
let init_ty = cx.type_func(&[], cx.type_void());
|
||||
let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty);
|
||||
|
||||
// We want LLVM's openmp-opt pass to pick up and optimize this module, since it covers both
|
||||
// openmp and offload optimizations.
|
||||
llvm::add_module_flag_u32(cx.llmod(), llvm::ModuleFlagMergeBehavior::Max, "openmp", 51);
|
||||
|
|
@ -58,7 +52,6 @@ impl<'ll> OffloadGlobals<'ll> {
|
|||
end_mapper,
|
||||
mapper_fn_ty,
|
||||
ident_t_global,
|
||||
init_rtls,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -91,6 +84,11 @@ pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) {
|
|||
let atexit = cx.type_func(&[cx.type_ptr()], cx.type_i32());
|
||||
let atexit_fn = declare_offload_fn(cx, "atexit", atexit);
|
||||
|
||||
// FIXME(offload): Drop this, once we fully automated our offload compilation pipeline, since
|
||||
// LLVM will initialize them for us if it sees gpu kernels being registered.
|
||||
let init_ty = cx.type_func(&[], cx.type_void());
|
||||
let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty);
|
||||
|
||||
let desc_ty = cx.type_func(&[], cx.type_void());
|
||||
let reg_name = ".omp_offloading.descriptor_reg";
|
||||
let unreg_name = ".omp_offloading.descriptor_unreg";
|
||||
|
|
@ -104,12 +102,14 @@ pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) {
|
|||
// define internal void @.omp_offloading.descriptor_reg() section ".text.startup" {
|
||||
// entry:
|
||||
// call void @__tgt_register_lib(ptr @.omp_offloading.descriptor)
|
||||
// call void @__tgt_init_all_rtls()
|
||||
// %0 = call i32 @atexit(ptr @.omp_offloading.descriptor_unreg)
|
||||
// ret void
|
||||
// }
|
||||
let bb = Builder::append_block(cx, desc_reg_fn, "entry");
|
||||
let mut a = Builder::build(cx, bb);
|
||||
a.call(reg_lib_decl, None, None, register_lib, &[omp_descriptor], None, None);
|
||||
a.call(init_ty, None, None, init_rtls, &[], None, None);
|
||||
a.call(atexit, None, None, atexit_fn, &[desc_unreg_fn], None, None);
|
||||
a.ret_void();
|
||||
|
||||
|
|
@ -345,7 +345,9 @@ impl KernelArgsTy {
|
|||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct OffloadKernelGlobals<'ll> {
|
||||
pub offload_sizes: &'ll llvm::Value,
|
||||
pub memtransfer_types: &'ll llvm::Value,
|
||||
pub memtransfer_begin: &'ll llvm::Value,
|
||||
pub memtransfer_kernel: &'ll llvm::Value,
|
||||
pub memtransfer_end: &'ll llvm::Value,
|
||||
pub region_id: &'ll llvm::Value,
|
||||
}
|
||||
|
||||
|
|
@ -423,18 +425,38 @@ pub(crate) fn gen_define_handling<'ll>(
|
|||
|
||||
let offload_entry_ty = offload_globals.offload_entry_ty;
|
||||
|
||||
// FIXME(Sa4dUs): add `OMP_MAP_TARGET_PARAM = 0x20` only if necessary
|
||||
let (sizes, transfer): (Vec<_>, Vec<_>) =
|
||||
metadata.iter().map(|m| (m.payload_size, m.mode.bits() | 0x20)).unzip();
|
||||
metadata.iter().map(|m| (m.payload_size, m.mode)).unzip();
|
||||
// Our begin mapper should only see simplified information about which args have to be
|
||||
// transferred to the device, the end mapper only about which args should be transferred back.
|
||||
// Any information beyond that makes it harder for LLVM's opt pass to evaluate whether it can
|
||||
// safely move (=optimize) the LLVM-IR location of this data transfer. Only the mapping types
|
||||
// mentioned below are handled, so make sure that we don't generate any other ones.
|
||||
let handled_mappings = MappingFlags::TO
|
||||
| MappingFlags::FROM
|
||||
| MappingFlags::TARGET_PARAM
|
||||
| MappingFlags::LITERAL
|
||||
| MappingFlags::IMPLICIT;
|
||||
for arg in &transfer {
|
||||
debug_assert!(!arg.contains_unknown_bits());
|
||||
debug_assert!(handled_mappings.contains(*arg));
|
||||
}
|
||||
|
||||
let valid_begin_mappings = MappingFlags::TO | MappingFlags::LITERAL | MappingFlags::IMPLICIT;
|
||||
let transfer_to: Vec<u64> =
|
||||
transfer.iter().map(|m| m.intersection(valid_begin_mappings).bits()).collect();
|
||||
let transfer_from: Vec<u64> =
|
||||
transfer.iter().map(|m| m.intersection(MappingFlags::FROM).bits()).collect();
|
||||
// FIXME(offload): add `OMP_MAP_TARGET_PARAM = 0x20` only if necessary
|
||||
let transfer_kernel = vec![MappingFlags::TARGET_PARAM.bits(); transfer_to.len()];
|
||||
|
||||
let offload_sizes = add_priv_unnamed_arr(&cx, &format!(".offload_sizes.{symbol}"), &sizes);
|
||||
// Here we figure out whether something needs to be copied to the gpu (=1), from the gpu (=2),
|
||||
// or both to and from the gpu (=3). Other values shouldn't affect us for now.
|
||||
// A non-mutable reference or pointer will be 1, an array that's not read, but fully overwritten
|
||||
// will be 2. For now, everything is 3, until we have our frontend set up.
|
||||
// 1+2+32: 1 (MapTo), 2 (MapFrom), 32 (Add one extra input ptr per function, to be used later).
|
||||
let memtransfer_types =
|
||||
add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}"), &transfer);
|
||||
let memtransfer_begin =
|
||||
add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}.begin"), &transfer_to);
|
||||
let memtransfer_kernel =
|
||||
add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}.kernel"), &transfer_kernel);
|
||||
let memtransfer_end =
|
||||
add_priv_unnamed_arr(&cx, &format!(".offload_maptypes.{symbol}.end"), &transfer_from);
|
||||
|
||||
// Next: For each function, generate these three entries. A weak constant,
|
||||
// the llvm.rodata entry name, and the llvm_offload_entries value
|
||||
|
|
@ -469,7 +491,13 @@ pub(crate) fn gen_define_handling<'ll>(
|
|||
|
||||
cx.add_compiler_used_global(offload_entry);
|
||||
|
||||
let result = OffloadKernelGlobals { offload_sizes, memtransfer_types, region_id };
|
||||
let result = OffloadKernelGlobals {
|
||||
offload_sizes,
|
||||
memtransfer_begin,
|
||||
memtransfer_kernel,
|
||||
memtransfer_end,
|
||||
region_id,
|
||||
};
|
||||
|
||||
// FIXME(Sa4dUs): use this global for constant offload sizes
|
||||
cx.add_compiler_used_global(result.offload_sizes);
|
||||
|
|
@ -535,7 +563,13 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
|
|||
offload_dims: &OffloadKernelDims<'ll>,
|
||||
) {
|
||||
let cx = builder.cx;
|
||||
let OffloadKernelGlobals { memtransfer_types, region_id, .. } = offload_data;
|
||||
let OffloadKernelGlobals {
|
||||
memtransfer_begin,
|
||||
memtransfer_kernel,
|
||||
memtransfer_end,
|
||||
region_id,
|
||||
..
|
||||
} = offload_data;
|
||||
let OffloadKernelDims { num_workgroups, threads_per_block, workgroup_dims, thread_dims } =
|
||||
offload_dims;
|
||||
|
||||
|
|
@ -608,12 +642,6 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
|
|||
geps.push(gep);
|
||||
}
|
||||
|
||||
let init_ty = cx.type_func(&[], cx.type_void());
|
||||
let init_rtls_decl = offload_globals.init_rtls;
|
||||
|
||||
// call void @__tgt_init_all_rtls()
|
||||
builder.call(init_ty, None, None, init_rtls_decl, &[], None, None);
|
||||
|
||||
for i in 0..num_args {
|
||||
let idx = cx.get_const_i32(i);
|
||||
let gep1 = builder.inbounds_gep(ty, a1, &[i32_0, idx]);
|
||||
|
|
@ -668,14 +696,14 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
|
|||
generate_mapper_call(
|
||||
builder,
|
||||
geps,
|
||||
memtransfer_types,
|
||||
memtransfer_begin,
|
||||
begin_mapper_decl,
|
||||
fn_ty,
|
||||
num_args,
|
||||
s_ident_t,
|
||||
);
|
||||
let values =
|
||||
KernelArgsTy::new(&cx, num_args, memtransfer_types, geps, workgroup_dims, thread_dims);
|
||||
KernelArgsTy::new(&cx, num_args, memtransfer_kernel, geps, workgroup_dims, thread_dims);
|
||||
|
||||
// Step 3)
|
||||
// Here we fill the KernelArgsTy, see the documentation above
|
||||
|
|
@ -701,7 +729,7 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
|
|||
generate_mapper_call(
|
||||
builder,
|
||||
geps,
|
||||
memtransfer_types,
|
||||
memtransfer_end,
|
||||
end_mapper_decl,
|
||||
fn_ty,
|
||||
num_args,
|
||||
|
|
|
|||
|
|
@ -479,7 +479,18 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>(
|
|||
AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span),
|
||||
},
|
||||
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
|
||||
_ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t),
|
||||
ty::Pat(base, _) => return type_di_node(cx, base),
|
||||
// FIXME(unsafe_binders): impl debug info
|
||||
ty::UnsafeBinder(_) => unimplemented!(),
|
||||
ty::Alias(..)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(..)
|
||||
| ty::Infer(_)
|
||||
| ty::Placeholder(_)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Error(_) => {
|
||||
bug!("debuginfo: unexpected type in type_di_node(): {:?}", t)
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -94,15 +94,13 @@ pub(crate) struct LtoBitcodeFromRlib {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub enum LlvmError<'a> {
|
||||
pub(crate) enum LlvmError<'a> {
|
||||
#[diag("could not write output to {$path}")]
|
||||
WriteOutput { path: &'a Path },
|
||||
#[diag("could not create LLVM TargetMachine for triple: {$triple}")]
|
||||
CreateTargetMachine { triple: SmallCStr },
|
||||
#[diag("failed to run LLVM passes")]
|
||||
RunLlvmPasses,
|
||||
#[diag("failed to serialize module {$name}")]
|
||||
SerializeModule { name: &'a str },
|
||||
#[diag("failed to write LLVM IR to {$path}")]
|
||||
WriteIr { path: &'a Path },
|
||||
#[diag("failed to prepare thin LTO context")]
|
||||
|
|
@ -115,8 +113,6 @@ pub enum LlvmError<'a> {
|
|||
PrepareThinLtoModule,
|
||||
#[diag("failed to parse bitcode for LTO module")]
|
||||
ParseBitcode,
|
||||
#[diag("failed to prepare autodiff: src: {$src}, target: {$target}, {$error}")]
|
||||
PrepareAutoDiff { src: String, target: String, error: String },
|
||||
}
|
||||
|
||||
pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String);
|
||||
|
|
@ -130,9 +126,6 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for WithLlvmError<'_> {
|
|||
"could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}"
|
||||
),
|
||||
RunLlvmPasses => inline_fluent!("failed to run LLVM passes: {$llvm_err}"),
|
||||
SerializeModule { .. } => {
|
||||
inline_fluent!("failed to serialize module {$name}: {$llvm_err}")
|
||||
}
|
||||
WriteIr { .. } => inline_fluent!("failed to write LLVM IR to {$path}: {$llvm_err}"),
|
||||
PrepareThinLtoContext => {
|
||||
inline_fluent!("failed to prepare thin LTO context: {$llvm_err}")
|
||||
|
|
@ -147,9 +140,6 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for WithLlvmError<'_> {
|
|||
inline_fluent!("failed to prepare thin LTO module: {$llvm_err}")
|
||||
}
|
||||
ParseBitcode => inline_fluent!("failed to parse bitcode for LTO module: {$llvm_err}"),
|
||||
PrepareAutoDiff { .. } => inline_fluent!(
|
||||
"failed to prepare autodiff: {$llvm_err}, src: {$src}, target: {$target}, {$error}"
|
||||
),
|
||||
};
|
||||
self.0
|
||||
.into_diag(dcx, level)
|
||||
|
|
@ -214,5 +204,5 @@ pub(crate) struct FixedX18InvalidArch<'a> {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.")]
|
||||
#[diag("`-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later")]
|
||||
pub(crate) struct SanitizerKcfiArityRequiresLLVM2100;
|
||||
|
|
|
|||
|
|
@ -646,10 +646,32 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
) -> Self::Value {
|
||||
let tcx = self.tcx();
|
||||
|
||||
// FIXME remove usage of fn_abi
|
||||
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
|
||||
assert!(!fn_abi.ret.is_indirect());
|
||||
let fn_ty = fn_abi.llvm_type(self);
|
||||
let fn_ty = instance.ty(tcx, self.typing_env());
|
||||
let fn_sig = match *fn_ty.kind() {
|
||||
ty::FnDef(def_id, args) => {
|
||||
tcx.instantiate_bound_regions_with_erased(tcx.fn_sig(def_id).instantiate(tcx, args))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert!(!fn_sig.c_variadic);
|
||||
|
||||
let ret_layout = self.layout_of(fn_sig.output());
|
||||
let llreturn_ty = if ret_layout.is_zst() {
|
||||
self.type_void()
|
||||
} else {
|
||||
ret_layout.immediate_llvm_type(self)
|
||||
};
|
||||
|
||||
let mut llargument_tys = Vec::with_capacity(fn_sig.inputs().len());
|
||||
for &arg in fn_sig.inputs() {
|
||||
let arg_layout = self.layout_of(arg);
|
||||
if arg_layout.is_zst() {
|
||||
continue;
|
||||
}
|
||||
llargument_tys.push(arg_layout.immediate_llvm_type(self));
|
||||
}
|
||||
|
||||
let fn_ty = self.type_func(&llargument_tys, llreturn_ty);
|
||||
|
||||
let fn_ptr = if let Some(&llfn) = self.intrinsic_instances.borrow().get(&instance) {
|
||||
llfn
|
||||
|
|
@ -665,12 +687,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
let llfn = declare_raw_fn(
|
||||
self,
|
||||
sym,
|
||||
fn_abi.llvm_cconv(self),
|
||||
llvm::CCallConv,
|
||||
llvm::UnnamedAddr::Global,
|
||||
llvm::Visibility::Default,
|
||||
fn_ty,
|
||||
);
|
||||
fn_abi.apply_attrs_llfn(self, llfn, Some(instance));
|
||||
|
||||
llfn
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
//! This API is completely unstable and subject to change.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![feature(assert_matches)]
|
||||
#![cfg_attr(bootstrap, feature(assert_matches))]
|
||||
#![feature(extern_types)]
|
||||
#![feature(file_buffered)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
@ -25,7 +25,6 @@ use std::path::PathBuf;
|
|||
use back::owned_target_machine::OwnedTargetMachine;
|
||||
use back::write::{create_informational_target_machine, create_target_machine};
|
||||
use context::SimpleCx;
|
||||
use errors::ParseTargetMachineConfig;
|
||||
use llvm_util::target_config;
|
||||
use rustc_ast::expand::allocator::AllocatorMethod;
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule};
|
||||
|
|
@ -36,6 +35,7 @@ use rustc_codegen_ssa::back::write::{
|
|||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_errors::{DiagCtxt, DiagCtxtHandle};
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
|
|
@ -152,7 +152,6 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
type Module = ModuleLlvm;
|
||||
type ModuleBuffer = back::lto::ModuleBuffer;
|
||||
type TargetMachine = OwnedTargetMachine;
|
||||
type TargetMachineError = crate::errors::LlvmError<'static>;
|
||||
type ThinData = back::lto::ThinData;
|
||||
type ThinBuffer = back::lto::ThinBuffer;
|
||||
fn print_pass_timings(&self) {
|
||||
|
|
@ -164,15 +163,19 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
print!("{stats}");
|
||||
}
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<LlvmCodegenBackend>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
) -> ModuleCodegen<Self::Module> {
|
||||
let mut module = back::lto::run_fat(
|
||||
cgcx,
|
||||
prof,
|
||||
shared_emitter,
|
||||
tm_factory,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
modules,
|
||||
|
|
@ -180,12 +183,13 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
|
||||
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
|
||||
let dcx = dcx.handle();
|
||||
back::lto::run_pass_manager(cgcx, dcx, &mut module, false);
|
||||
back::lto::run_pass_manager(cgcx, prof, dcx, &mut module, false);
|
||||
|
||||
module
|
||||
}
|
||||
fn run_thin_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
|
|
@ -194,6 +198,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
|
||||
back::lto::run_thin(
|
||||
cgcx,
|
||||
prof,
|
||||
dcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
|
|
@ -202,27 +207,31 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
)
|
||||
}
|
||||
fn optimize(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
module: &mut ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
) {
|
||||
back::write::optimize(cgcx, shared_emitter, module, config)
|
||||
back::write::optimize(cgcx, prof, shared_emitter, module, config)
|
||||
}
|
||||
fn optimize_thin(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<LlvmCodegenBackend>,
|
||||
thin: ThinModule<Self>,
|
||||
) -> ModuleCodegen<Self::Module> {
|
||||
back::lto::optimize_thin_module(cgcx, shared_emitter, thin)
|
||||
back::lto::optimize_thin_module(cgcx, prof, shared_emitter, tm_factory, thin)
|
||||
}
|
||||
fn codegen(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
module: ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
) -> CompiledModule {
|
||||
back::write::codegen(cgcx, shared_emitter, module, config)
|
||||
back::write::codegen(cgcx, prof, shared_emitter, module, config)
|
||||
}
|
||||
fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
|
||||
back::lto::prepare_thin(module)
|
||||
|
|
@ -440,22 +449,9 @@ impl ModuleLlvm {
|
|||
}
|
||||
}
|
||||
|
||||
fn tm_from_cgcx(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
name: &str,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
) -> OwnedTargetMachine {
|
||||
let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, name);
|
||||
match (cgcx.tm_factory)(tm_factory_config) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
dcx.emit_fatal(ParseTargetMachineConfig(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
cgcx: &CodegenContext,
|
||||
tm_factory: TargetMachineFactoryFn<LlvmCodegenBackend>,
|
||||
name: &CStr,
|
||||
buffer: &[u8],
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
|
|
@ -464,7 +460,7 @@ impl ModuleLlvm {
|
|||
let llcx = llvm::LLVMContextCreate();
|
||||
llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool());
|
||||
let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx);
|
||||
let tm = ModuleLlvm::tm_from_cgcx(cgcx, name.to_str().unwrap(), dcx);
|
||||
let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap()));
|
||||
|
||||
ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ rustc_trait_selection = { path = "../rustc_trait_selection" }
|
|||
serde_json = "1.0.59"
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
tempfile = "3.2"
|
||||
thin-vec = "0.2.12"
|
||||
thorin-dwp = "0.9"
|
||||
tracing = "0.1"
|
||||
wasm-encoder = "0.219"
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@ use std::fmt;
|
|||
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_errors::{DiagArgValue, IntoDiagArg};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::{AttributeKind, CguFields, CguKind};
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_hir::{self as hir, find_attr};
|
||||
use rustc_middle::mir::mono::CodegenUnitNameBuilder;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::errors;
|
||||
|
|
@ -63,9 +63,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTr
|
|||
},
|
||||
};
|
||||
|
||||
for attr in tcx.hir_attrs(rustc_hir::CRATE_HIR_ID) {
|
||||
ams.check_attr(attr);
|
||||
}
|
||||
ams.check_attrs(tcx.hir_attrs(rustc_hir::CRATE_HIR_ID));
|
||||
|
||||
set_reuse(&mut ams.cgu_reuse_tracker);
|
||||
|
||||
|
|
@ -89,109 +87,91 @@ struct AssertModuleSource<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> AssertModuleSource<'tcx> {
|
||||
fn check_attr(&mut self, attr: &hir::Attribute) {
|
||||
let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) {
|
||||
(CguReuse::PreLto, ComparisonKind::AtLeast)
|
||||
} else if attr.has_name(sym::rustc_partition_codegened) {
|
||||
(CguReuse::No, ComparisonKind::Exact)
|
||||
} else if attr.has_name(sym::rustc_expected_cgu_reuse) {
|
||||
match self.field(attr, sym::kind) {
|
||||
sym::no => (CguReuse::No, ComparisonKind::Exact),
|
||||
sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
|
||||
sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
|
||||
sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
|
||||
other => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_fatal(errors::UnknownReuseKind { span: attr.span(), kind: other });
|
||||
}
|
||||
fn check_attrs(&mut self, attrs: &[hir::Attribute]) {
|
||||
for &(span, cgu_fields) in find_attr!(attrs,
|
||||
AttributeKind::RustcCguTestAttr(e) => e)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
let (expected_reuse, comp_kind) = match cgu_fields {
|
||||
CguFields::PartitionReused { .. } => (CguReuse::PreLto, ComparisonKind::AtLeast),
|
||||
CguFields::PartitionCodegened { .. } => (CguReuse::No, ComparisonKind::Exact),
|
||||
CguFields::ExpectedCguReuse { kind, .. } => match kind {
|
||||
CguKind::No => (CguReuse::No, ComparisonKind::Exact),
|
||||
CguKind::PreDashLto => (CguReuse::PreLto, ComparisonKind::Exact),
|
||||
CguKind::PostDashLto => (CguReuse::PostLto, ComparisonKind::Exact),
|
||||
CguKind::Any => (CguReuse::PreLto, ComparisonKind::AtLeast),
|
||||
},
|
||||
};
|
||||
let (CguFields::ExpectedCguReuse { cfg, module, .. }
|
||||
| CguFields::PartitionCodegened { cfg, module }
|
||||
| CguFields::PartitionReused { cfg, module }) = cgu_fields;
|
||||
|
||||
if !self.tcx.sess.opts.unstable_opts.query_dep_graph {
|
||||
self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span });
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !self.tcx.sess.opts.unstable_opts.query_dep_graph {
|
||||
self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span() });
|
||||
}
|
||||
if !self.check_config(cfg) {
|
||||
debug!("check_attr: config does not match, ignoring attr");
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.check_config(attr) {
|
||||
debug!("check_attr: config does not match, ignoring attr");
|
||||
return;
|
||||
}
|
||||
let user_path = module.as_str();
|
||||
let crate_name = self.tcx.crate_name(LOCAL_CRATE);
|
||||
let crate_name = crate_name.as_str();
|
||||
|
||||
let user_path = self.field(attr, sym::module).to_string();
|
||||
let crate_name = self.tcx.crate_name(LOCAL_CRATE).to_string();
|
||||
if !user_path.starts_with(&crate_name) {
|
||||
self.tcx.dcx().emit_fatal(errors::MalformedCguName { span, user_path, crate_name });
|
||||
}
|
||||
|
||||
if !user_path.starts_with(&crate_name) {
|
||||
self.tcx.dcx().emit_fatal(errors::MalformedCguName {
|
||||
span: attr.span(),
|
||||
user_path,
|
||||
crate_name,
|
||||
});
|
||||
}
|
||||
// Split of the "special suffix" if there is one.
|
||||
let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind('.') {
|
||||
(&user_path[..index], Some(&user_path[index + 1..]))
|
||||
} else {
|
||||
(&user_path[..], None)
|
||||
};
|
||||
|
||||
// Split of the "special suffix" if there is one.
|
||||
let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind('.') {
|
||||
(&user_path[..index], Some(&user_path[index + 1..]))
|
||||
} else {
|
||||
(&user_path[..], None)
|
||||
};
|
||||
let mut iter = user_path.split('-');
|
||||
|
||||
let mut iter = user_path.split('-');
|
||||
// Remove the crate name
|
||||
assert_eq!(iter.next().unwrap(), crate_name);
|
||||
|
||||
// Remove the crate name
|
||||
assert_eq!(iter.next().unwrap(), crate_name);
|
||||
let cgu_path_components = iter.collect::<Vec<_>>();
|
||||
|
||||
let cgu_path_components = iter.collect::<Vec<_>>();
|
||||
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx);
|
||||
let cgu_name = cgu_name_builder.build_cgu_name(
|
||||
LOCAL_CRATE,
|
||||
cgu_path_components,
|
||||
cgu_special_suffix,
|
||||
);
|
||||
|
||||
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx);
|
||||
let cgu_name =
|
||||
cgu_name_builder.build_cgu_name(LOCAL_CRATE, cgu_path_components, cgu_special_suffix);
|
||||
debug!("mapping '{user_path}' to cgu name '{cgu_name}'");
|
||||
|
||||
debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name);
|
||||
if !self.available_cgus.contains(&cgu_name) {
|
||||
let cgu_names: Vec<&str> =
|
||||
self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord();
|
||||
self.tcx.dcx().emit_err(errors::NoModuleNamed {
|
||||
span,
|
||||
user_path,
|
||||
cgu_name,
|
||||
cgu_names: cgu_names.join(", "),
|
||||
});
|
||||
}
|
||||
|
||||
if !self.available_cgus.contains(&cgu_name) {
|
||||
let cgu_names: Vec<&str> =
|
||||
self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord();
|
||||
self.tcx.dcx().emit_err(errors::NoModuleNamed {
|
||||
span: attr.span(),
|
||||
user_path,
|
||||
self.cgu_reuse_tracker.set_expectation(
|
||||
cgu_name,
|
||||
cgu_names: cgu_names.join(", "),
|
||||
});
|
||||
user_path,
|
||||
span,
|
||||
expected_reuse,
|
||||
comp_kind,
|
||||
);
|
||||
}
|
||||
|
||||
self.cgu_reuse_tracker.set_expectation(
|
||||
cgu_name,
|
||||
user_path,
|
||||
attr.span(),
|
||||
expected_reuse,
|
||||
comp_kind,
|
||||
);
|
||||
}
|
||||
|
||||
fn field(&self, attr: &hir::Attribute, name: Symbol) -> Symbol {
|
||||
for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
|
||||
if item.has_name(name) {
|
||||
if let Some(value) = item.value_str() {
|
||||
return value;
|
||||
} else {
|
||||
self.tcx.dcx().emit_fatal(errors::FieldAssociatedValueExpected {
|
||||
span: item.span(),
|
||||
name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span(), name });
|
||||
}
|
||||
|
||||
/// Scan for a `cfg="foo"` attribute and check whether we have a
|
||||
/// cfg flag called `foo`.
|
||||
fn check_config(&self, attr: &hir::Attribute) -> bool {
|
||||
fn check_config(&self, value: Symbol) -> bool {
|
||||
let config = &self.tcx.sess.psess.config;
|
||||
let value = self.field(attr, sym::cfg);
|
||||
debug!("check_config(config={:?}, value={:?})", config, value);
|
||||
if config.iter().any(|&(name, _)| name == value) {
|
||||
debug!("check_config: matched");
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::ffi::{OsStr, OsString};
|
|||
use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, io, iter, mem, str};
|
||||
use std::{env, iter, mem, str};
|
||||
|
||||
use find_msvc_tools;
|
||||
use rustc_hir::attrs::WindowsSubsystemKind;
|
||||
|
|
@ -809,7 +809,7 @@ impl<'a> Linker for GccLinker<'a> {
|
|||
|
||||
if self.sess.target.is_like_darwin {
|
||||
// Write a plain, newline-separated list of symbols
|
||||
let res: io::Result<()> = try {
|
||||
let res = try {
|
||||
let mut f = File::create_buffered(&path)?;
|
||||
for (sym, _) in symbols {
|
||||
debug!(" _{sym}");
|
||||
|
|
@ -821,7 +821,7 @@ impl<'a> Linker for GccLinker<'a> {
|
|||
}
|
||||
self.link_arg("-exported_symbols_list").link_arg(path);
|
||||
} else if self.sess.target.is_like_windows {
|
||||
let res: io::Result<()> = try {
|
||||
let res = try {
|
||||
let mut f = File::create_buffered(&path)?;
|
||||
|
||||
// .def file similar to MSVC one but without LIBRARY section
|
||||
|
|
@ -845,7 +845,7 @@ impl<'a> Linker for GccLinker<'a> {
|
|||
self.link_arg("--export").link_arg(sym);
|
||||
}
|
||||
} else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris {
|
||||
let res: io::Result<()> = try {
|
||||
let res = try {
|
||||
let mut f = File::create_buffered(&path)?;
|
||||
writeln!(f, "{{")?;
|
||||
for (sym, _) in symbols {
|
||||
|
|
@ -860,7 +860,7 @@ impl<'a> Linker for GccLinker<'a> {
|
|||
self.link_arg("--dynamic-list").link_arg(path);
|
||||
} else {
|
||||
// Write an LD version script
|
||||
let res: io::Result<()> = try {
|
||||
let res = try {
|
||||
let mut f = File::create_buffered(&path)?;
|
||||
writeln!(f, "{{")?;
|
||||
if !symbols.is_empty() {
|
||||
|
|
@ -1139,7 +1139,7 @@ impl<'a> Linker for MsvcLinker<'a> {
|
|||
}
|
||||
|
||||
let path = tmpdir.join("lib.def");
|
||||
let res: io::Result<()> = try {
|
||||
let res = try {
|
||||
let mut f = File::create_buffered(&path)?;
|
||||
|
||||
// Start off with the standard module name header and then go
|
||||
|
|
@ -1735,7 +1735,7 @@ impl<'a> Linker for AixLinker<'a> {
|
|||
symbols: &[(String, SymbolExportKind)],
|
||||
) {
|
||||
let path = tmpdir.join("list.exp");
|
||||
let res: io::Result<()> = try {
|
||||
let res = try {
|
||||
let mut f = File::create_buffered(&path)?;
|
||||
// FIXME: use llvm-nm to generate export list.
|
||||
for (symbol, _) in symbols {
|
||||
|
|
@ -2135,7 +2135,7 @@ impl<'a> Linker for BpfLinker<'a> {
|
|||
symbols: &[(String, SymbolExportKind)],
|
||||
) {
|
||||
let path = tmpdir.join("symbols");
|
||||
let res: io::Result<()> = try {
|
||||
let res = try {
|
||||
let mut f = File::create_buffered(&path)?;
|
||||
for (sym, _) in symbols {
|
||||
writeln!(f, "{sym}")?;
|
||||
|
|
|
|||
|
|
@ -127,10 +127,7 @@ pub(super) fn exported_symbols_for_lto(
|
|||
symbols_below_threshold
|
||||
}
|
||||
|
||||
pub(super) fn check_lto_allowed<B: WriteBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
) {
|
||||
pub(super) fn check_lto_allowed(cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>) {
|
||||
if cgcx.lto == Lto::ThinLocal {
|
||||
// Crate local LTO is always allowed
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -23,13 +23,15 @@ use rustc_hir::find_attr;
|
|||
use rustc_incremental::{
|
||||
copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
|
||||
};
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
use rustc_metadata::fs::copy_to_stdout;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{
|
||||
self, CrateType, Lto, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath,
|
||||
self, CrateType, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes,
|
||||
SwitchWithOptPath,
|
||||
};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{FileName, InnerSpan, Span, SpanData};
|
||||
|
|
@ -49,7 +51,7 @@ use crate::{
|
|||
const PRE_LTO_BC_EXT: &str = "pre-lto.bc";
|
||||
|
||||
/// What kind of object file to emit.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, PartialEq, Encodable, Decodable)]
|
||||
pub enum EmitObj {
|
||||
// No object file.
|
||||
None,
|
||||
|
|
@ -63,7 +65,7 @@ pub enum EmitObj {
|
|||
}
|
||||
|
||||
/// What kind of llvm bitcode section to embed in an object file.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, PartialEq, Encodable, Decodable)]
|
||||
pub enum BitcodeSection {
|
||||
// No bitcode section.
|
||||
None,
|
||||
|
|
@ -73,6 +75,7 @@ pub enum BitcodeSection {
|
|||
}
|
||||
|
||||
/// Module-specific configuration for `optimize_and_codegen`.
|
||||
#[derive(Encodable, Decodable)]
|
||||
pub struct ModuleConfig {
|
||||
/// Names of additional optimization passes to run.
|
||||
pub passes: Vec<String>,
|
||||
|
|
@ -287,10 +290,7 @@ pub struct TargetMachineFactoryConfig {
|
|||
}
|
||||
|
||||
impl TargetMachineFactoryConfig {
|
||||
pub fn new(
|
||||
cgcx: &CodegenContext<impl WriteBackendMethods>,
|
||||
module_name: &str,
|
||||
) -> TargetMachineFactoryConfig {
|
||||
pub fn new(cgcx: &CodegenContext, module_name: &str) -> TargetMachineFactoryConfig {
|
||||
let split_dwarf_file = if cgcx.target_can_use_split_dwarf {
|
||||
cgcx.output_filenames.split_dwarf_path(
|
||||
cgcx.split_debuginfo,
|
||||
|
|
@ -313,19 +313,17 @@ impl TargetMachineFactoryConfig {
|
|||
|
||||
pub type TargetMachineFactoryFn<B> = Arc<
|
||||
dyn Fn(
|
||||
DiagCtxtHandle<'_>,
|
||||
TargetMachineFactoryConfig,
|
||||
) -> Result<
|
||||
<B as WriteBackendMethods>::TargetMachine,
|
||||
<B as WriteBackendMethods>::TargetMachineError,
|
||||
> + Send
|
||||
) -> <B as WriteBackendMethods>::TargetMachine
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
|
||||
/// Additional resources used by optimize_and_codegen (not module specific)
|
||||
#[derive(Clone)]
|
||||
pub struct CodegenContext<B: WriteBackendMethods> {
|
||||
#[derive(Clone, Encodable, Decodable)]
|
||||
pub struct CodegenContext {
|
||||
// Resources needed when running LTO
|
||||
pub prof: SelfProfilerRef,
|
||||
pub lto: Lto,
|
||||
pub use_linker_plugin_lto: bool,
|
||||
pub dylib_lto: bool,
|
||||
|
|
@ -337,7 +335,8 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
|||
pub output_filenames: Arc<OutputFilenames>,
|
||||
pub invocation_temp: Option<String>,
|
||||
pub module_config: Arc<ModuleConfig>,
|
||||
pub tm_factory: TargetMachineFactoryFn<B>,
|
||||
pub opt_level: OptLevel,
|
||||
pub backend_features: Vec<String>,
|
||||
pub msvc_imps_needed: bool,
|
||||
pub is_pe_coff: bool,
|
||||
pub target_can_use_split_dwarf: bool,
|
||||
|
|
@ -364,17 +363,19 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
|||
}
|
||||
|
||||
fn generate_thin_lto_work<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
needs_thin_lto: Vec<(String, B::ThinBuffer)>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Vec<(ThinLtoWorkItem<B>, u64)> {
|
||||
let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work");
|
||||
let _prof_timer = prof.generic_activity("codegen_thin_generate_lto_work");
|
||||
|
||||
let (lto_modules, copy_jobs) = B::run_thin_lto(
|
||||
cgcx,
|
||||
prof,
|
||||
dcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
|
|
@ -399,9 +400,9 @@ fn generate_thin_lto_work<B: ExtraBackendMethods>(
|
|||
.collect()
|
||||
}
|
||||
|
||||
struct CompiledModules {
|
||||
modules: Vec<CompiledModule>,
|
||||
allocator_module: Option<CompiledModule>,
|
||||
pub struct CompiledModules {
|
||||
pub modules: Vec<CompiledModule>,
|
||||
pub allocator_module: Option<CompiledModule>,
|
||||
}
|
||||
|
||||
enum MaybeLtoModules<B: WriteBackendMethods> {
|
||||
|
|
@ -410,7 +411,7 @@ enum MaybeLtoModules<B: WriteBackendMethods> {
|
|||
allocator_module: Option<CompiledModule>,
|
||||
},
|
||||
FatLto {
|
||||
cgcx: CodegenContext<B>,
|
||||
cgcx: CodegenContext,
|
||||
exported_symbols_for_lto: Arc<Vec<String>>,
|
||||
each_linked_rlib_file_for_lto: Vec<PathBuf>,
|
||||
needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
|
|
@ -418,7 +419,7 @@ enum MaybeLtoModules<B: WriteBackendMethods> {
|
|||
Vec<(SerializedModule<<B as WriteBackendMethods>::ModuleBuffer>, WorkProduct)>,
|
||||
},
|
||||
ThinLto {
|
||||
cgcx: CodegenContext<B>,
|
||||
cgcx: CodegenContext,
|
||||
exported_symbols_for_lto: Arc<Vec<String>>,
|
||||
each_linked_rlib_file_for_lto: Vec<PathBuf>,
|
||||
needs_thin_lto: Vec<(String, <B as WriteBackendMethods>::ThinBuffer)>,
|
||||
|
|
@ -534,7 +535,7 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
|
|||
work_products
|
||||
}
|
||||
|
||||
fn produce_final_output_artifacts(
|
||||
pub fn produce_final_output_artifacts(
|
||||
sess: &Session,
|
||||
compiled_modules: &CompiledModules,
|
||||
crate_output: &OutputFilenames,
|
||||
|
|
@ -842,13 +843,14 @@ pub(crate) fn compute_per_cgu_lto_type(
|
|||
}
|
||||
|
||||
fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: SharedEmitter,
|
||||
mut module: ModuleCodegen<B::Module>,
|
||||
) -> WorkItemResult<B> {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*module.name);
|
||||
let _timer = prof.generic_activity_with_arg("codegen_module_optimize", &*module.name);
|
||||
|
||||
B::optimize(cgcx, &shared_emitter, &mut module, &cgcx.module_config);
|
||||
B::optimize(cgcx, prof, &shared_emitter, &mut module, &cgcx.module_config);
|
||||
|
||||
// After we've done the initial round of optimizations we need to
|
||||
// decide whether to synchronously codegen this module or ship it
|
||||
|
|
@ -869,7 +871,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
|||
|
||||
match lto_type {
|
||||
ComputedLtoType::No => {
|
||||
let module = B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config);
|
||||
let module = B::codegen(cgcx, &prof, &shared_emitter, module, &cgcx.module_config);
|
||||
WorkItemResult::Finished(module)
|
||||
}
|
||||
ComputedLtoType::Thin => {
|
||||
|
|
@ -897,14 +899,14 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
|||
}
|
||||
}
|
||||
|
||||
fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
fn execute_copy_from_cache_work_item(
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: SharedEmitter,
|
||||
module: CachedModuleCodegen,
|
||||
) -> CompiledModule {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name);
|
||||
let _timer =
|
||||
prof.generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name);
|
||||
|
||||
let dcx = DiagCtxt::new(Box::new(shared_emitter));
|
||||
let dcx = dcx.handle();
|
||||
|
|
@ -986,14 +988,16 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
|||
}
|
||||
|
||||
fn do_fat_lto<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<B>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
mut needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
) -> CompiledModule {
|
||||
let _timer = cgcx.prof.verbose_generic_activity("LLVM_fatlto");
|
||||
let _timer = prof.verbose_generic_activity("LLVM_fatlto");
|
||||
|
||||
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
|
||||
let dcx = dcx.handle();
|
||||
|
|
@ -1006,17 +1010,21 @@ fn do_fat_lto<B: ExtraBackendMethods>(
|
|||
|
||||
let module = B::run_and_optimize_fat_lto(
|
||||
cgcx,
|
||||
prof,
|
||||
&shared_emitter,
|
||||
tm_factory,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
needs_fat_lto,
|
||||
);
|
||||
B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config)
|
||||
B::codegen(cgcx, prof, &shared_emitter, module, &cgcx.module_config)
|
||||
}
|
||||
|
||||
fn do_thin_lto<'a, B: ExtraBackendMethods>(
|
||||
cgcx: &'a CodegenContext<B>,
|
||||
fn do_thin_lto<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<B>,
|
||||
exported_symbols_for_lto: Arc<Vec<String>>,
|
||||
each_linked_rlib_for_lto: Vec<PathBuf>,
|
||||
needs_thin_lto: Vec<(String, <B as WriteBackendMethods>::ThinBuffer)>,
|
||||
|
|
@ -1025,7 +1033,7 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
|
|||
WorkProduct,
|
||||
)>,
|
||||
) -> Vec<CompiledModule> {
|
||||
let _timer = cgcx.prof.verbose_generic_activity("LLVM_thinlto");
|
||||
let _timer = prof.verbose_generic_activity("LLVM_thinlto");
|
||||
|
||||
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
|
||||
let dcx = dcx.handle();
|
||||
|
|
@ -1053,8 +1061,9 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
|
|||
// bunch of work items onto our queue to do LTO. This all
|
||||
// happens on the coordinator thread but it's very quick so
|
||||
// we don't worry about tokens.
|
||||
for (work, cost) in generate_thin_lto_work(
|
||||
for (work, cost) in generate_thin_lto_work::<B>(
|
||||
cgcx,
|
||||
prof,
|
||||
dcx,
|
||||
&exported_symbols_for_lto,
|
||||
&each_linked_rlib_for_lto,
|
||||
|
|
@ -1097,7 +1106,14 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
|
|||
while used_token_count < tokens.len() + 1
|
||||
&& let Some((item, _)) = work_items.pop()
|
||||
{
|
||||
spawn_thin_lto_work(&cgcx, shared_emitter.clone(), coordinator_send.clone(), item);
|
||||
spawn_thin_lto_work(
|
||||
&cgcx,
|
||||
prof,
|
||||
shared_emitter.clone(),
|
||||
Arc::clone(&tm_factory),
|
||||
coordinator_send.clone(),
|
||||
item,
|
||||
);
|
||||
used_token_count += 1;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1158,14 +1174,16 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
|
|||
}
|
||||
|
||||
fn execute_thin_lto_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<B>,
|
||||
module: lto::ThinModule<B>,
|
||||
) -> CompiledModule {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", module.name());
|
||||
let _timer = prof.generic_activity_with_arg("codegen_module_perform_lto", module.name());
|
||||
|
||||
let module = B::optimize_thin(cgcx, &shared_emitter, module);
|
||||
B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config)
|
||||
let module = B::optimize_thin(cgcx, prof, &shared_emitter, tm_factory, module);
|
||||
B::codegen(cgcx, prof, &shared_emitter, module, &cgcx.module_config)
|
||||
}
|
||||
|
||||
/// Messages sent to the coordinator.
|
||||
|
|
@ -1265,6 +1283,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
coordinator_send: Sender<Message<B>>,
|
||||
) -> thread::JoinHandle<Result<MaybeLtoModules<B>, ()>> {
|
||||
let sess = tcx.sess;
|
||||
let prof = sess.prof.clone();
|
||||
|
||||
let mut each_linked_rlib_for_lto = Vec::new();
|
||||
let mut each_linked_rlib_file_for_lto = Vec::new();
|
||||
|
|
@ -1292,8 +1311,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
})
|
||||
.expect("failed to spawn helper thread");
|
||||
|
||||
let ol = tcx.backend_optimization_level(());
|
||||
let backend_features = tcx.global_backend_features(());
|
||||
let opt_level = tcx.backend_optimization_level(());
|
||||
let backend_features = tcx.global_backend_features(()).clone();
|
||||
let tm_factory = backend.target_machine_factory(tcx.sess, opt_level, &backend_features);
|
||||
|
||||
let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir {
|
||||
let result = fs::create_dir_all(dir).and_then(|_| dir.canonicalize());
|
||||
|
|
@ -1305,7 +1325,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
None
|
||||
};
|
||||
|
||||
let cgcx = CodegenContext::<B> {
|
||||
let cgcx = CodegenContext {
|
||||
crate_types: tcx.crate_types().to_vec(),
|
||||
lto: sess.lto(),
|
||||
use_linker_plugin_lto: sess.opts.cg.linker_plugin_lto.enabled(),
|
||||
|
|
@ -1314,13 +1334,13 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
fewer_names: sess.fewer_names(),
|
||||
save_temps: sess.opts.cg.save_temps,
|
||||
time_trace: sess.opts.unstable_opts.llvm_time_trace,
|
||||
prof: sess.prof.clone(),
|
||||
remark: sess.opts.cg.remark.clone(),
|
||||
remark_dir,
|
||||
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
|
||||
output_filenames: Arc::clone(tcx.output_filenames(())),
|
||||
module_config: regular_config,
|
||||
tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features),
|
||||
opt_level,
|
||||
backend_features,
|
||||
msvc_imps_needed: msvc_imps_needed(tcx),
|
||||
is_pe_coff: tcx.sess.target.is_like_windows,
|
||||
target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(),
|
||||
|
|
@ -1514,7 +1534,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
let mut llvm_start_time: Option<VerboseTimingGuard<'_>> = None;
|
||||
|
||||
if let Some(allocator_module) = &mut allocator_module {
|
||||
B::optimize(&cgcx, &shared_emitter, allocator_module, &allocator_config);
|
||||
B::optimize(&cgcx, &prof, &shared_emitter, allocator_module, &allocator_config);
|
||||
}
|
||||
|
||||
// Run the message loop while there's still anything that needs message
|
||||
|
|
@ -1554,6 +1574,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
main_thread_state = MainThreadState::Lending;
|
||||
spawn_work(
|
||||
&cgcx,
|
||||
&prof,
|
||||
shared_emitter.clone(),
|
||||
coordinator_send.clone(),
|
||||
&mut llvm_start_time,
|
||||
|
|
@ -1578,6 +1599,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
main_thread_state = MainThreadState::Lending;
|
||||
spawn_work(
|
||||
&cgcx,
|
||||
&prof,
|
||||
shared_emitter.clone(),
|
||||
coordinator_send.clone(),
|
||||
&mut llvm_start_time,
|
||||
|
|
@ -1620,6 +1642,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
{
|
||||
spawn_work(
|
||||
&cgcx,
|
||||
&prof,
|
||||
shared_emitter.clone(),
|
||||
coordinator_send.clone(),
|
||||
&mut llvm_start_time,
|
||||
|
|
@ -1776,9 +1799,11 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
assert!(needs_fat_lto.is_empty());
|
||||
|
||||
if cgcx.lto == Lto::ThinLocal {
|
||||
compiled_modules.extend(do_thin_lto(
|
||||
compiled_modules.extend(do_thin_lto::<B>(
|
||||
&cgcx,
|
||||
&prof,
|
||||
shared_emitter.clone(),
|
||||
tm_factory,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
needs_thin_lto,
|
||||
|
|
@ -1803,7 +1828,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
Ok(MaybeLtoModules::NoLto {
|
||||
modules: compiled_modules,
|
||||
allocator_module: allocator_module.map(|allocator_module| {
|
||||
B::codegen(&cgcx, &shared_emitter, allocator_module, &allocator_config)
|
||||
B::codegen(&cgcx, &prof, &shared_emitter, allocator_module, &allocator_config)
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
|
@ -1872,23 +1897,25 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
pub(crate) struct WorkerFatalError;
|
||||
|
||||
fn spawn_work<'a, B: ExtraBackendMethods>(
|
||||
cgcx: &'a CodegenContext<B>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &'a SelfProfilerRef,
|
||||
shared_emitter: SharedEmitter,
|
||||
coordinator_send: Sender<Message<B>>,
|
||||
llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
|
||||
work: WorkItem<B>,
|
||||
) {
|
||||
if llvm_start_time.is_none() {
|
||||
*llvm_start_time = Some(cgcx.prof.verbose_generic_activity("LLVM_passes"));
|
||||
*llvm_start_time = Some(prof.verbose_generic_activity("LLVM_passes"));
|
||||
}
|
||||
|
||||
let cgcx = cgcx.clone();
|
||||
let prof = prof.clone();
|
||||
|
||||
B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || {
|
||||
let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work {
|
||||
WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, shared_emitter, m),
|
||||
WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, &prof, shared_emitter, m),
|
||||
WorkItem::CopyPostLtoArtifacts(m) => WorkItemResult::Finished(
|
||||
execute_copy_from_cache_work_item(&cgcx, shared_emitter, m),
|
||||
execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m),
|
||||
),
|
||||
}));
|
||||
|
||||
|
|
@ -1909,20 +1936,25 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
.expect("failed to spawn work thread");
|
||||
}
|
||||
|
||||
fn spawn_thin_lto_work<'a, B: ExtraBackendMethods>(
|
||||
cgcx: &'a CodegenContext<B>,
|
||||
fn spawn_thin_lto_work<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<B>,
|
||||
coordinator_send: Sender<ThinLtoMessage>,
|
||||
work: ThinLtoWorkItem<B>,
|
||||
) {
|
||||
let cgcx = cgcx.clone();
|
||||
let prof = prof.clone();
|
||||
|
||||
B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || {
|
||||
let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work {
|
||||
ThinLtoWorkItem::CopyPostLtoArtifacts(m) => {
|
||||
execute_copy_from_cache_work_item(&cgcx, shared_emitter, m)
|
||||
execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m)
|
||||
}
|
||||
ThinLtoWorkItem::ThinLto(m) => {
|
||||
execute_thin_lto_work_item(&cgcx, &prof, shared_emitter, tm_factory, m)
|
||||
}
|
||||
ThinLtoWorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, shared_emitter, m),
|
||||
}));
|
||||
|
||||
let msg = match result {
|
||||
|
|
@ -1981,11 +2013,7 @@ impl SharedEmitter {
|
|||
}
|
||||
|
||||
impl Emitter for SharedEmitter {
|
||||
fn emit_diagnostic(
|
||||
&mut self,
|
||||
mut diag: rustc_errors::DiagInner,
|
||||
_registry: &rustc_errors::registry::Registry,
|
||||
) {
|
||||
fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) {
|
||||
// Check that we aren't missing anything interesting when converting to
|
||||
// the cut-down local `DiagInner`.
|
||||
assert!(!diag.span.has_span_labels());
|
||||
|
|
@ -2163,34 +2191,54 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
|
|||
each_linked_rlib_file_for_lto,
|
||||
needs_fat_lto,
|
||||
lto_import_only_modules,
|
||||
} => CompiledModules {
|
||||
modules: vec![do_fat_lto(
|
||||
&cgcx,
|
||||
shared_emitter,
|
||||
&exported_symbols_for_lto,
|
||||
&each_linked_rlib_file_for_lto,
|
||||
needs_fat_lto,
|
||||
lto_import_only_modules,
|
||||
)],
|
||||
allocator_module: None,
|
||||
},
|
||||
} => {
|
||||
let tm_factory = self.backend.target_machine_factory(
|
||||
sess,
|
||||
cgcx.opt_level,
|
||||
&cgcx.backend_features,
|
||||
);
|
||||
|
||||
CompiledModules {
|
||||
modules: vec![do_fat_lto(
|
||||
&cgcx,
|
||||
&sess.prof,
|
||||
shared_emitter,
|
||||
tm_factory,
|
||||
&exported_symbols_for_lto,
|
||||
&each_linked_rlib_file_for_lto,
|
||||
needs_fat_lto,
|
||||
lto_import_only_modules,
|
||||
)],
|
||||
allocator_module: None,
|
||||
}
|
||||
}
|
||||
MaybeLtoModules::ThinLto {
|
||||
cgcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
needs_thin_lto,
|
||||
lto_import_only_modules,
|
||||
} => CompiledModules {
|
||||
modules: do_thin_lto(
|
||||
&cgcx,
|
||||
shared_emitter,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
needs_thin_lto,
|
||||
lto_import_only_modules,
|
||||
),
|
||||
allocator_module: None,
|
||||
},
|
||||
} => {
|
||||
let tm_factory = self.backend.target_machine_factory(
|
||||
sess,
|
||||
cgcx.opt_level,
|
||||
&cgcx.backend_features,
|
||||
);
|
||||
|
||||
CompiledModules {
|
||||
modules: do_thin_lto::<B>(
|
||||
&cgcx,
|
||||
&sess.prof,
|
||||
shared_emitter,
|
||||
tm_factory,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_file_for_lto,
|
||||
needs_thin_lto,
|
||||
lto_import_only_modules,
|
||||
),
|
||||
allocator_module: None,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
shared_emitter_main.check(sess, true);
|
||||
|
|
|
|||
|
|
@ -1111,7 +1111,7 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) ->
|
|||
// know that later). If we are not doing LTO, there is only one optimized
|
||||
// version of each module, so we re-use that.
|
||||
let dep_node = cgu.codegen_dep_node(tcx);
|
||||
tcx.dep_graph.assert_dep_node_not_yet_allocated_in_current_session(&dep_node, || {
|
||||
tcx.dep_graph.assert_dep_node_not_yet_allocated_in_current_session(tcx.sess, &dep_node, || {
|
||||
format!(
|
||||
"CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
|
||||
cgu.name()
|
||||
|
|
|
|||
|
|
@ -42,14 +42,6 @@ pub(crate) struct CguNotRecorded<'a> {
|
|||
pub cgu_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("unknown cgu-reuse-kind `{$kind}` specified")]
|
||||
pub(crate) struct UnknownReuseKind {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("found CGU-reuse attribute but `-Zquery-dep-graph` was not specified")]
|
||||
pub(crate) struct MissingQueryDepGraph {
|
||||
|
|
@ -59,13 +51,13 @@ pub(crate) struct MissingQueryDepGraph {
|
|||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(
|
||||
"found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case)."
|
||||
"found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case)"
|
||||
)]
|
||||
pub(crate) struct MalformedCguName {
|
||||
pub(crate) struct MalformedCguName<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub user_path: String,
|
||||
pub crate_name: String,
|
||||
pub user_path: &'a str,
|
||||
pub crate_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -78,22 +70,6 @@ pub(crate) struct NoModuleNamed<'a> {
|
|||
pub cgu_names: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("associated value expected for `{$name}`")]
|
||||
pub(crate) struct FieldAssociatedValueExpected {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("no field `{$name}`")]
|
||||
pub(crate) struct NoField {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("failed to write lib.def file: {$error}")]
|
||||
pub(crate) struct LibDefWriteFailure {
|
||||
|
|
@ -154,14 +130,14 @@ pub(crate) struct CopyPathBuf {
|
|||
// Reports Paths using `Debug` implementation rather than Path's `Display` implementation.
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("could not copy {$from} to {$to}: {$error}")]
|
||||
pub struct CopyPath<'a> {
|
||||
pub(crate) struct CopyPath<'a> {
|
||||
from: DebugArgPath<'a>,
|
||||
to: DebugArgPath<'a>,
|
||||
error: Error,
|
||||
}
|
||||
|
||||
impl<'a> CopyPath<'a> {
|
||||
pub fn new(from: &'a Path, to: &'a Path, error: Error) -> CopyPath<'a> {
|
||||
pub(crate) fn new(from: &'a Path, to: &'a Path, error: Error) -> CopyPath<'a> {
|
||||
CopyPath { from: DebugArgPath(from), to: DebugArgPath(to), error }
|
||||
}
|
||||
}
|
||||
|
|
@ -178,19 +154,19 @@ impl IntoDiagArg for DebugArgPath<'_> {
|
|||
#[diag(
|
||||
"option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty"
|
||||
)]
|
||||
pub struct BinaryOutputToTty {
|
||||
pub(crate) struct BinaryOutputToTty {
|
||||
pub shorthand: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("ignoring emit path because multiple .{$extension} files were produced")]
|
||||
pub struct IgnoringEmitPath {
|
||||
pub(crate) struct IgnoringEmitPath {
|
||||
pub extension: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("ignoring -o because multiple .{$extension} files were produced")]
|
||||
pub struct IgnoringOutput {
|
||||
pub(crate) struct IgnoringOutput {
|
||||
pub extension: &'static str,
|
||||
}
|
||||
|
||||
|
|
@ -562,12 +538,12 @@ pub(crate) struct SelfContainedLinkerMissing;
|
|||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(
|
||||
"please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option."
|
||||
"please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option"
|
||||
)]
|
||||
pub(crate) struct CheckInstalledVisualStudio;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("VS Code is a different product, and is not sufficient.")]
|
||||
#[diag("VS Code is a different product, and is not sufficient")]
|
||||
pub(crate) struct InsufficientVSCodeProduct;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -610,13 +586,13 @@ pub(crate) struct LinkerFileStem;
|
|||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(
|
||||
"link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms."
|
||||
"link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms"
|
||||
)]
|
||||
pub(crate) struct StaticLibraryNativeArtifacts;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(
|
||||
"native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms."
|
||||
"native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms"
|
||||
)]
|
||||
pub(crate) struct StaticLibraryNativeArtifactsToFile<'a> {
|
||||
pub path: &'a Path,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// tidy-alphabetical-start
|
||||
#![feature(assert_matches)]
|
||||
#![cfg_attr(bootstrap, feature(assert_matches))]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(file_buffered)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
|
||||
use crate::back::lto::{SerializedModule, ThinModule};
|
||||
use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter};
|
||||
use crate::back::write::{
|
||||
CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn,
|
||||
};
|
||||
use crate::{CompiledModule, ModuleCodegen};
|
||||
|
||||
pub trait WriteBackendMethods: Clone + 'static {
|
||||
type Module: Send + Sync;
|
||||
type TargetMachine;
|
||||
type TargetMachineError;
|
||||
type ModuleBuffer: ModuleBufferMethods;
|
||||
type ThinData: Send + Sync;
|
||||
type ThinBuffer: ThinBufferMethods;
|
||||
|
|
@ -18,8 +20,10 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
/// Performs fat LTO by merging all modules into a single one, running autodiff
|
||||
/// if necessary and running any further optimizations
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<Self>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
|
|
@ -28,7 +32,8 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
/// lists, one of the modules that need optimization and another for modules that
|
||||
/// can simply be copied over from the incr. comp. cache.
|
||||
fn run_thin_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
|
|
@ -38,18 +43,22 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
fn print_pass_timings(&self);
|
||||
fn print_statistics(&self);
|
||||
fn optimize(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
module: &mut ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
);
|
||||
fn optimize_thin(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
tm_factory: TargetMachineFactoryFn<Self>,
|
||||
thin: ThinModule<Self>,
|
||||
) -> ModuleCodegen<Self::Module>;
|
||||
fn codegen(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
cgcx: &CodegenContext,
|
||||
prof: &SelfProfilerRef,
|
||||
shared_emitter: &SharedEmitter,
|
||||
module: ModuleCodegen<Self::Module>,
|
||||
config: &ModuleConfig,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ rustc_apfloat = "0.2.0"
|
|||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_infer = { path = "../rustc_infer" }
|
||||
|
|
|
|||
|
|
@ -1,507 +0,0 @@
|
|||
const_eval_address_space_full =
|
||||
there are no more free addresses in the address space
|
||||
|
||||
const_eval_alignment_check_failed =
|
||||
{$msg ->
|
||||
[AccessedPtr] accessing memory
|
||||
*[other] accessing memory based on pointer
|
||||
} with alignment {$has}, but alignment {$required} is required
|
||||
|
||||
const_eval_already_reported =
|
||||
an error has already been reported elsewhere (this should not usually be printed)
|
||||
const_eval_assume_false =
|
||||
`assume` called with `false`
|
||||
|
||||
const_eval_bad_pointer_op = {$operation ->
|
||||
[MemoryAccess] memory access failed
|
||||
[InboundsPointerArithmetic] in-bounds pointer arithmetic failed
|
||||
*[Dereferenceable] pointer not dereferenceable
|
||||
}
|
||||
const_eval_bad_pointer_op_attempting = {const_eval_bad_pointer_op}: {$operation ->
|
||||
[MemoryAccess] attempting to access {$inbounds_size ->
|
||||
[1] 1 byte
|
||||
*[x] {$inbounds_size} bytes
|
||||
}
|
||||
[InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size ->
|
||||
[1] 1 byte
|
||||
*[x] {$inbounds_size} bytes
|
||||
}
|
||||
*[Dereferenceable] pointer must {$inbounds_size ->
|
||||
[0] point to some allocation
|
||||
[1] be dereferenceable for 1 byte
|
||||
*[x] be dereferenceable for {$inbounds_size} bytes
|
||||
}
|
||||
}
|
||||
|
||||
const_eval_bounds_check_failed =
|
||||
indexing out of bounds: the len is {$len} but the index is {$index}
|
||||
const_eval_call_nonzero_intrinsic =
|
||||
`{$name}` called on 0
|
||||
|
||||
const_eval_closure_call =
|
||||
closures need an RFC before allowed to be called in {const_eval_const_context}s
|
||||
const_eval_closure_fndef_not_const =
|
||||
function defined here, but it is not `const`
|
||||
|
||||
const_eval_consider_dereferencing =
|
||||
consider dereferencing here
|
||||
|
||||
const_eval_const_accesses_mut_global =
|
||||
constant accesses mutable global memory
|
||||
|
||||
const_eval_const_context = {$kind ->
|
||||
[const] constant
|
||||
[static] static
|
||||
[const_fn] constant function
|
||||
*[other] {""}
|
||||
}
|
||||
|
||||
const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global
|
||||
.note = use `const_make_global` to turn allocated pointers into immutable globals before returning
|
||||
|
||||
const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc}
|
||||
|
||||
const_eval_const_make_global_ptr_is_non_heap = pointer passed to `const_make_global` does not point to a heap allocation: {$ptr}
|
||||
|
||||
const_eval_const_make_global_with_dangling_ptr = pointer passed to `const_make_global` is dangling: {$ptr}
|
||||
|
||||
const_eval_const_make_global_with_offset = making {$ptr} global which does not point to the beginning of an object
|
||||
|
||||
const_eval_copy_nonoverlapping_overlapping =
|
||||
`copy_nonoverlapping` called on overlapping ranges
|
||||
|
||||
const_eval_dangling_int_pointer =
|
||||
{const_eval_bad_pointer_op_attempting}, but got {$pointer} which is a dangling pointer (it has no provenance)
|
||||
const_eval_dangling_null_pointer =
|
||||
{const_eval_bad_pointer_op_attempting}, but got null pointer
|
||||
|
||||
const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind}
|
||||
const_eval_dead_local =
|
||||
accessing a dead local variable
|
||||
const_eval_dealloc_immutable =
|
||||
deallocating immutable allocation {$alloc}
|
||||
|
||||
const_eval_dealloc_incorrect_layout =
|
||||
incorrect layout on deallocation: {$alloc} has size {$size} and alignment {$align}, but gave size {$size_found} and alignment {$align_found}
|
||||
|
||||
const_eval_dealloc_kind_mismatch =
|
||||
deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation
|
||||
|
||||
const_eval_deref_function_pointer =
|
||||
accessing {$allocation} which contains a function
|
||||
const_eval_deref_typeid_pointer =
|
||||
accessing {$allocation} which contains a `TypeId`
|
||||
const_eval_deref_vtable_pointer =
|
||||
accessing {$allocation} which contains a vtable
|
||||
const_eval_division_by_zero =
|
||||
dividing by zero
|
||||
const_eval_division_overflow =
|
||||
overflow in signed division (dividing MIN by -1)
|
||||
|
||||
const_eval_dyn_call_not_a_method =
|
||||
`dyn` call trying to call something that is not a method
|
||||
|
||||
const_eval_error = evaluation of `{$instance}` failed {$num_frames ->
|
||||
[0] here
|
||||
*[other] inside this call
|
||||
}
|
||||
|
||||
const_eval_exact_div_has_remainder =
|
||||
exact_div: {$a} cannot be divided by {$b} without remainder
|
||||
|
||||
const_eval_extern_static =
|
||||
cannot access extern static `{$did}`
|
||||
const_eval_extern_type_field = `extern type` field does not have a known offset
|
||||
|
||||
const_eval_fn_ptr_call =
|
||||
function pointers need an RFC before allowed to be called in {const_eval_const_context}s
|
||||
const_eval_frame_note = {$times ->
|
||||
[0] {const_eval_frame_note_inner}
|
||||
*[other] [... {$times} additional calls {const_eval_frame_note_inner} ...]
|
||||
}
|
||||
|
||||
const_eval_frame_note_inner = inside {$where_ ->
|
||||
[closure] closure
|
||||
[instance] `{$instance}`
|
||||
*[other] {""}
|
||||
}
|
||||
|
||||
const_eval_frame_note_last = the failure occurred here
|
||||
|
||||
const_eval_incompatible_arg_types =
|
||||
calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}
|
||||
|
||||
const_eval_incompatible_calling_conventions =
|
||||
calling a function with calling convention "{$callee_conv}" using calling convention "{$caller_conv}"
|
||||
|
||||
const_eval_incompatible_return_types =
|
||||
calling a function with return type {$callee_ty} passing return place of type {$caller_ty}
|
||||
|
||||
const_eval_interior_mutable_borrow_escaping =
|
||||
interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
|
||||
.label = this borrow of an interior mutable value refers to such a temporary
|
||||
.note = temporaries in constants and statics can have their lifetime extended until the end of the program
|
||||
.note2 = to avoid accidentally creating global mutable state, such temporaries must be immutable
|
||||
.help = if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
|
||||
|
||||
const_eval_intern_kind = {$kind ->
|
||||
[static] static
|
||||
[static_mut] mutable static
|
||||
[const] constant
|
||||
[promoted] promoted
|
||||
*[other] {""}
|
||||
}
|
||||
|
||||
const_eval_interrupted = compilation was interrupted
|
||||
|
||||
const_eval_invalid_align_details =
|
||||
invalid align passed to `{$name}`: {$align} is {$err_kind ->
|
||||
[not_power_of_two] not a power of 2
|
||||
[too_large] too large
|
||||
*[other] {""}
|
||||
}
|
||||
|
||||
const_eval_invalid_bool =
|
||||
interpreting an invalid 8-bit value as a bool: 0x{$value}
|
||||
const_eval_invalid_char =
|
||||
interpreting an invalid 32-bit value as a char: 0x{$value}
|
||||
const_eval_invalid_dealloc =
|
||||
deallocating {$alloc_id}, which is {$kind ->
|
||||
[fn] a function
|
||||
[vtable] a vtable
|
||||
[static_mem] static memory
|
||||
*[other] {""}
|
||||
}
|
||||
|
||||
const_eval_invalid_function_pointer =
|
||||
using {$pointer} as function pointer but it does not point to a function
|
||||
const_eval_invalid_meta =
|
||||
invalid metadata in wide pointer: total size is bigger than largest supported object
|
||||
const_eval_invalid_meta_slice =
|
||||
invalid metadata in wide pointer: slice is bigger than largest supported object
|
||||
|
||||
const_eval_invalid_niched_enum_variant_written =
|
||||
trying to set discriminant of a {$ty} to the niched variant, but the value does not match
|
||||
|
||||
const_eval_invalid_str =
|
||||
this string is not valid UTF-8: {$err}
|
||||
const_eval_invalid_tag =
|
||||
enum value has invalid tag: {$tag}
|
||||
const_eval_invalid_transmute =
|
||||
transmuting from {$src_bytes}-byte type to {$dest_bytes}-byte type: `{$src}` -> `{$dest}`
|
||||
|
||||
const_eval_invalid_uninit_bytes =
|
||||
reading memory at {$alloc}{$access}, but memory is uninitialized at {$uninit}, and this operation requires initialized memory
|
||||
const_eval_invalid_uninit_bytes_unknown =
|
||||
using uninitialized data, but this operation requires initialized memory
|
||||
|
||||
const_eval_invalid_vtable_pointer =
|
||||
using {$pointer} as vtable pointer but it does not point to a vtable
|
||||
|
||||
const_eval_invalid_vtable_trait =
|
||||
using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected
|
||||
|
||||
const_eval_lazy_lock =
|
||||
consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
|
||||
|
||||
const_eval_live_drop =
|
||||
destructor of `{$dropped_ty}` cannot be evaluated at compile-time
|
||||
.label = the destructor for this type cannot be evaluated in {const_eval_const_context}s
|
||||
.dropped_at_label = value is dropped here
|
||||
|
||||
const_eval_long_running =
|
||||
constant evaluation is taking a long time
|
||||
.note = this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
|
||||
If your compilation actually takes a long time, you can safely allow the lint.
|
||||
.label = the const evaluator is currently interpreting this expression
|
||||
.help = the constant being evaluated
|
||||
|
||||
const_eval_memory_exhausted =
|
||||
tried to allocate more memory than available to compiler
|
||||
|
||||
const_eval_modified_global =
|
||||
modifying a static's initial value from another static's initializer
|
||||
|
||||
const_eval_mutable_borrow_escaping =
|
||||
mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
|
||||
.label = this mutable borrow refers to such a temporary
|
||||
.note = temporaries in constants and statics can have their lifetime extended until the end of the program
|
||||
.note2 = to avoid accidentally creating global mutable state, such temporaries must be immutable
|
||||
.help = if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
|
||||
|
||||
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
|
||||
|
||||
const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
|
||||
|
||||
const_eval_non_const_await =
|
||||
cannot convert `{$ty}` into a future in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_closure =
|
||||
cannot call {$non_or_conditionally}-const closure in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_deref_coercion =
|
||||
cannot perform {$non_or_conditionally}-const deref coercion on `{$ty}` in {const_eval_const_context}s
|
||||
.note = attempting to deref into `{$target_ty}`
|
||||
.target_note = deref defined here
|
||||
|
||||
const_eval_non_const_fmt_macro_call =
|
||||
cannot call {$non_or_conditionally}-const formatting macro in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_fn_call =
|
||||
cannot call {$non_or_conditionally}-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_for_loop_into_iter =
|
||||
cannot use `for` loop on `{$ty}` in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_impl =
|
||||
impl defined here, but it is not `const`
|
||||
|
||||
const_eval_non_const_intrinsic =
|
||||
cannot call non-const intrinsic `{$name}` in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_match_eq = cannot match on `{$ty}` in {const_eval_const_context}s
|
||||
.note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es
|
||||
|
||||
const_eval_non_const_operator =
|
||||
cannot call {$non_or_conditionally}-const operator in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_question_branch =
|
||||
`?` is not allowed on `{$ty}` in {const_eval_const_context}s
|
||||
const_eval_non_const_question_from_residual =
|
||||
`?` is not allowed on `{$ty}` in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_try_block_from_output =
|
||||
`try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
|
||||
|
||||
const_eval_not_enough_caller_args =
|
||||
calling a function with fewer arguments than it requires
|
||||
|
||||
const_eval_offset_from_different_allocations =
|
||||
`{$name}` called on two different pointers that are not both derived from the same allocation
|
||||
const_eval_offset_from_out_of_bounds =
|
||||
`{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation
|
||||
const_eval_offset_from_overflow =
|
||||
`{$name}` called when first pointer is too far ahead of second
|
||||
const_eval_offset_from_underflow =
|
||||
`{$name}` called when first pointer is too far before second
|
||||
const_eval_offset_from_unsigned_overflow =
|
||||
`ptr_offset_from_unsigned` called when first pointer has smaller {$is_addr ->
|
||||
[true] address
|
||||
*[false] offset
|
||||
} than second: {$a_offset} < {$b_offset}
|
||||
|
||||
const_eval_overflow_arith =
|
||||
arithmetic overflow in `{$intrinsic}`
|
||||
const_eval_overflow_shift =
|
||||
overflowing shift by {$shift_amount} in `{$intrinsic}`
|
||||
|
||||
const_eval_panic = evaluation panicked: {$msg}
|
||||
|
||||
const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str`
|
||||
|
||||
const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind}
|
||||
.note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
|
||||
|
||||
const_eval_partial_pointer_read =
|
||||
unable to read parts of a pointer from memory at {$ptr}
|
||||
const_eval_pointer_arithmetic_overflow =
|
||||
overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
|
||||
|
||||
const_eval_pointer_out_of_bounds =
|
||||
{const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$ptr_offset_is_neg ->
|
||||
[true] points to before the beginning of the allocation
|
||||
*[false] {$inbounds_size_is_neg ->
|
||||
[false] {$alloc_size_minus_ptr_offset ->
|
||||
[0] is at or beyond the end of the allocation of size {$alloc_size ->
|
||||
[1] 1 byte
|
||||
*[x] {$alloc_size} bytes
|
||||
}
|
||||
[1] is only 1 byte from the end of the allocation
|
||||
*[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
|
||||
}
|
||||
*[true] {$ptr_offset_abs ->
|
||||
[0] is at the beginning of the allocation
|
||||
*[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const_eval_pointer_use_after_free =
|
||||
{const_eval_bad_pointer_op}: {$alloc_id} has been freed, so this pointer is dangling
|
||||
const_eval_ptr_as_bytes_1 =
|
||||
this code performed an operation that depends on the underlying bytes representing a pointer
|
||||
const_eval_ptr_as_bytes_2 =
|
||||
the absolute address of a pointer is not known at compile-time, so such operations are not supported
|
||||
|
||||
const_eval_range = in the range {$lo}..={$hi}
|
||||
const_eval_range_lower = greater or equal to {$lo}
|
||||
const_eval_range_singular = equal to {$lo}
|
||||
const_eval_range_upper = less or equal to {$hi}
|
||||
const_eval_range_wrapping = less or equal to {$hi}, or greater or equal to {$lo}
|
||||
const_eval_raw_bytes = the raw bytes of the constant (size: {$size}, align: {$align}) {"{"}{$bytes}{"}"}
|
||||
|
||||
const_eval_raw_ptr_comparison =
|
||||
pointers cannot be reliably compared during const eval
|
||||
.note = see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
|
||||
|
||||
const_eval_raw_ptr_to_int =
|
||||
pointers cannot be cast to integers during const eval
|
||||
.note = at compile-time, pointers do not have an integer value
|
||||
.note2 = avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior
|
||||
|
||||
const_eval_read_pointer_as_int =
|
||||
unable to turn pointer into integer
|
||||
const_eval_realloc_or_alloc_with_offset =
|
||||
{$kind ->
|
||||
[dealloc] deallocating
|
||||
[realloc] reallocating
|
||||
*[other] {""}
|
||||
} {$ptr} which does not point to the beginning of an object
|
||||
|
||||
const_eval_recursive_static = encountered static that tried to access itself during initialization
|
||||
|
||||
const_eval_remainder_by_zero =
|
||||
calculating the remainder with a divisor of zero
|
||||
const_eval_remainder_overflow =
|
||||
overflow in signed remainder (dividing MIN by -1)
|
||||
const_eval_scalar_size_mismatch =
|
||||
scalar size mismatch: expected {$target_size} bytes but got {$data_size} bytes instead
|
||||
const_eval_size_overflow =
|
||||
overflow computing total size of `{$name}`
|
||||
|
||||
const_eval_stack_frame_limit_reached =
|
||||
reached the configured maximum number of stack frames
|
||||
|
||||
const_eval_thread_local_access =
|
||||
thread-local statics cannot be accessed at compile-time
|
||||
|
||||
const_eval_thread_local_static =
|
||||
cannot access thread local static `{$did}`
|
||||
const_eval_too_generic =
|
||||
encountered overly generic constant
|
||||
const_eval_too_many_caller_args =
|
||||
calling a function with more arguments than it expected
|
||||
|
||||
const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s
|
||||
|
||||
const_eval_unallowed_heap_allocations =
|
||||
allocations are not allowed in {const_eval_const_context}s
|
||||
.label = allocation not allowed in {const_eval_const_context}s
|
||||
.teach_note =
|
||||
The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created.
|
||||
|
||||
const_eval_unallowed_inline_asm =
|
||||
inline assembly is not allowed in {const_eval_const_context}s
|
||||
|
||||
const_eval_unallowed_op_in_const_context =
|
||||
{$msg}
|
||||
|
||||
const_eval_uninhabited_enum_variant_read =
|
||||
read discriminant of an uninhabited enum variant
|
||||
const_eval_uninhabited_enum_variant_written =
|
||||
writing discriminant of an uninhabited enum variant
|
||||
|
||||
const_eval_unmarked_const_item_exposed = `{$def_path}` cannot be (indirectly) exposed to stable
|
||||
.help = either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]`
|
||||
const_eval_unmarked_intrinsic_exposed = intrinsic `{$def_path}` cannot be (indirectly) exposed to stable
|
||||
.help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval)
|
||||
|
||||
const_eval_unreachable = entering unreachable code
|
||||
const_eval_unreachable_unwind =
|
||||
unwinding past a stack frame that does not allow unwinding
|
||||
|
||||
const_eval_unsized_local = unsized locals are not supported
|
||||
const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn
|
||||
const_eval_unstable_const_trait = `{$def_path}` is not yet stable as a const trait
|
||||
const_eval_unstable_in_stable_exposed =
|
||||
const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]`
|
||||
.is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features
|
||||
.unstable_sugg = if the {$is_function_call2 ->
|
||||
[true] caller
|
||||
*[false] function
|
||||
} is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]`
|
||||
|
||||
const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic
|
||||
const_eval_unstable_intrinsic_suggestion = add `#![feature({$feature})]` to the crate attributes to enable
|
||||
|
||||
const_eval_unterminated_c_string =
|
||||
reading a null-terminated string starting at {$pointer} with no null found before end of allocation
|
||||
|
||||
const_eval_unwind_past_top =
|
||||
unwinding past the topmost frame of the stack
|
||||
|
||||
## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`.
|
||||
## (We'd love to sort this differently to make that more clear but tidy won't let us...)
|
||||
const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
|
||||
|
||||
const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
|
||||
const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
|
||||
const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)
|
||||
const_eval_validation_dangling_ref_no_provenance = {$front_matter}: encountered a dangling reference ({$pointer} has no provenance)
|
||||
const_eval_validation_dangling_ref_out_of_bounds = {$front_matter}: encountered a dangling reference (going beyond the bounds of its allocation)
|
||||
const_eval_validation_dangling_ref_use_after_free = {$front_matter}: encountered a dangling reference (use-after-free)
|
||||
|
||||
const_eval_validation_expected_bool = expected a boolean
|
||||
const_eval_validation_expected_box = expected a box
|
||||
const_eval_validation_expected_char = expected a unicode scalar value
|
||||
const_eval_validation_expected_enum_tag = expected a valid enum tag
|
||||
const_eval_validation_expected_float = expected a floating point number
|
||||
const_eval_validation_expected_fn_ptr = expected a function pointer
|
||||
const_eval_validation_expected_init_scalar = expected initialized scalar value
|
||||
const_eval_validation_expected_int = expected an integer
|
||||
const_eval_validation_expected_raw_ptr = expected a raw pointer
|
||||
const_eval_validation_expected_ref = expected a reference
|
||||
const_eval_validation_expected_str = expected a string
|
||||
|
||||
const_eval_validation_failure =
|
||||
it is undefined behavior to use this value
|
||||
|
||||
const_eval_validation_failure_note =
|
||||
the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
|
||||
const_eval_validation_front_matter_invalid_value = constructing invalid value
|
||||
const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path}
|
||||
|
||||
const_eval_validation_invalid_bool = {$front_matter}: encountered {$value}, but expected a boolean
|
||||
const_eval_validation_invalid_box_meta = {$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object
|
||||
const_eval_validation_invalid_box_slice_meta = {$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object
|
||||
const_eval_validation_invalid_char = {$front_matter}: encountered {$value}, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)
|
||||
|
||||
const_eval_validation_invalid_enum_tag = {$front_matter}: encountered {$value}, but expected a valid enum tag
|
||||
const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, but expected a function pointer
|
||||
const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object
|
||||
const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
|
||||
const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
|
||||
const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}`
|
||||
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
|
||||
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
|
||||
const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero
|
||||
const_eval_validation_null_box = {$front_matter}: encountered a {$maybe ->
|
||||
[true] maybe-null
|
||||
*[false] null
|
||||
} box
|
||||
const_eval_validation_null_fn_ptr = {$front_matter}: encountered a {$maybe ->
|
||||
[true] maybe-null
|
||||
*[false] null
|
||||
} function pointer
|
||||
const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe ->
|
||||
[true] maybe-null
|
||||
*[false] null
|
||||
} reference
|
||||
const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range}
|
||||
const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers
|
||||
const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected}
|
||||
const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer with unknown absolute address, but expected something that is definitely {$in_range}
|
||||
const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty}
|
||||
const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes})
|
||||
const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes})
|
||||
const_eval_validation_uninhabited_enum_variant = {$front_matter}: encountered an uninhabited enum variant
|
||||
const_eval_validation_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}`
|
||||
const_eval_validation_uninit = {$front_matter}: encountered uninitialized memory, but {$expected}
|
||||
const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in read-only memory
|
||||
|
||||
const_eval_write_through_immutable_pointer =
|
||||
writing through a pointer that was derived from a shared (immutable) reference
|
||||
|
||||
const_eval_write_to_read_only =
|
||||
writing to {$allocation} which is read-only
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use hir::{ConstContext, LangItem};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, MultiSpan};
|
||||
use rustc_errors::{Applicability, Diag, MultiSpan, inline_fluent};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
|
|
@ -23,7 +23,7 @@ use rustc_trait_selection::traits::SelectionContext;
|
|||
use tracing::debug;
|
||||
|
||||
use super::ConstCx;
|
||||
use crate::{errors, fluent_generated};
|
||||
use crate::errors;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Status {
|
||||
|
|
@ -181,7 +181,9 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
|||
);
|
||||
|
||||
if let ConstContext::Static(_) = ccx.const_kind() {
|
||||
err.note(fluent_generated::const_eval_lazy_lock);
|
||||
err.note(inline_fluent!(
|
||||
"consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`"
|
||||
));
|
||||
}
|
||||
|
||||
err
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ where
|
|||
let uneval = match constant.const_ {
|
||||
Const::Ty(_, ct) => match ct.kind() {
|
||||
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) => None,
|
||||
// Unevaluated consts in MIR bodies don't have associated MIR (e.g. `#[type_const]`).
|
||||
// Unevaluated consts in MIR bodies don't have associated MIR (e.g. `type const`).
|
||||
ty::ConstKind::Unevaluated(_) => None,
|
||||
// FIXME(mgca): Investigate whether using `None` for `ConstKind::Value` is overly
|
||||
// strict, and if instead we should be doing some kind of value-based analysis.
|
||||
|
|
|
|||
|
|
@ -43,21 +43,41 @@ pub enum ConstEvalErrKind {
|
|||
impl MachineStopType for ConstEvalErrKind {
|
||||
fn diagnostic_message(&self) -> DiagMessage {
|
||||
use ConstEvalErrKind::*;
|
||||
use rustc_errors::inline_fluent;
|
||||
|
||||
use crate::fluent_generated::*;
|
||||
match self {
|
||||
ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
|
||||
ModifiedGlobal => const_eval_modified_global,
|
||||
Panic { .. } => const_eval_panic,
|
||||
RecursiveStatic => const_eval_recursive_static,
|
||||
AssertFailure(x) => x.diagnostic_message(),
|
||||
WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer,
|
||||
ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => {
|
||||
const_eval_const_make_global_ptr_already_made_global
|
||||
ConstAccessesMutGlobal => "constant accesses mutable global memory".into(),
|
||||
ModifiedGlobal => {
|
||||
"modifying a static's initial value from another static's initializer".into()
|
||||
}
|
||||
Panic { .. } => inline_fluent!("evaluation panicked: {$msg}"),
|
||||
RecursiveStatic => {
|
||||
"encountered static that tried to access itself during initialization".into()
|
||||
}
|
||||
AssertFailure(x) => x.diagnostic_message(),
|
||||
WriteThroughImmutablePointer => {
|
||||
inline_fluent!(
|
||||
"writing through a pointer that was derived from a shared (immutable) reference"
|
||||
)
|
||||
}
|
||||
ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => {
|
||||
inline_fluent!(
|
||||
"attempting to call `const_make_global` twice on the same allocation {$alloc}"
|
||||
)
|
||||
}
|
||||
ConstMakeGlobalPtrIsNonHeap(_) => {
|
||||
inline_fluent!(
|
||||
"pointer passed to `const_make_global` does not point to a heap allocation: {$ptr}"
|
||||
)
|
||||
}
|
||||
ConstMakeGlobalWithDanglingPtr(_) => {
|
||||
inline_fluent!("pointer passed to `const_make_global` is dangling: {$ptr}")
|
||||
}
|
||||
ConstMakeGlobalWithOffset(_) => {
|
||||
inline_fluent!(
|
||||
"making {$ptr} global which does not point to the beginning of an object"
|
||||
)
|
||||
}
|
||||
ConstMakeGlobalPtrIsNonHeap(_) => const_eval_const_make_global_ptr_is_non_heap,
|
||||
ConstMakeGlobalWithDanglingPtr(_) => const_eval_const_make_global_with_dangling_ptr,
|
||||
ConstMakeGlobalWithOffset(_) => const_eval_const_make_global_with_offset,
|
||||
}
|
||||
}
|
||||
fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::sync::atomic::Ordering::Relaxed;
|
|||
|
||||
use either::{Left, Right};
|
||||
use rustc_abi::{self as abi, BackendRepr};
|
||||
use rustc_errors::E0080;
|
||||
use rustc_errors::{E0080, inline_fluent};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo, ReportedErrorInfo};
|
||||
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
|
||||
|
|
@ -394,7 +394,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
|
|||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> Result<R, ErrorHandled> {
|
||||
let def = cid.instance.def.def_id();
|
||||
// #[type_const] don't have bodys
|
||||
// `type const` don't have bodys
|
||||
debug_assert!(!tcx.is_type_const(def), "CTFE tried to evaluate type-const: {:?}", def);
|
||||
|
||||
let is_static = tcx.is_static(def);
|
||||
|
|
@ -467,7 +467,15 @@ fn report_eval_error<'tcx>(
|
|||
let num_frames = frames.len();
|
||||
// FIXME(oli-obk): figure out how to use structured diagnostics again.
|
||||
diag.code(E0080);
|
||||
diag.span_label(span, crate::fluent_generated::const_eval_error);
|
||||
diag.span_label(
|
||||
span,
|
||||
inline_fluent!(
|
||||
"evaluation of `{$instance}` failed {$num_frames ->
|
||||
[0] here
|
||||
*[other] inside this call
|
||||
}"
|
||||
),
|
||||
);
|
||||
for frame in frames {
|
||||
diag.subdiagnostic(frame);
|
||||
}
|
||||
|
|
@ -506,8 +514,8 @@ fn report_validation_error<'tcx>(
|
|||
move |diag, span, frames| {
|
||||
// FIXME(oli-obk): figure out how to use structured diagnostics again.
|
||||
diag.code(E0080);
|
||||
diag.span_label(span, crate::fluent_generated::const_eval_validation_failure);
|
||||
diag.note(crate::fluent_generated::const_eval_validation_failure_note);
|
||||
diag.span_label(span, "it is undefined behavior to use this value");
|
||||
diag.note("the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.");
|
||||
for frame in frames {
|
||||
diag.subdiagnostic(frame);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::hash::Hash;
|
|||
use rustc_abi::{Align, Size};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
|
||||
use rustc_errors::inline_fluent;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
|
||||
use rustc_middle::mir::AssertMessage;
|
||||
|
|
@ -19,7 +20,6 @@ use tracing::debug;
|
|||
|
||||
use super::error::*;
|
||||
use crate::errors::{LongRunning, LongRunningWarn};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::interpret::{
|
||||
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
|
||||
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar,
|
||||
|
|
@ -489,7 +489,13 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
let align = match Align::from_bytes(align) {
|
||||
Ok(a) => a,
|
||||
Err(err) => throw_ub_custom!(
|
||||
fluent::const_eval_invalid_align_details,
|
||||
inline_fluent!(
|
||||
"invalid align passed to `{$name}`: {$align} is {$err_kind ->
|
||||
[not_power_of_two] not a power of 2
|
||||
[too_large] too large
|
||||
*[other] {\"\"}
|
||||
}"
|
||||
),
|
||||
name = "const_allocate",
|
||||
err_kind = err.diag_ident(),
|
||||
align = err.align()
|
||||
|
|
@ -513,7 +519,13 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
let align = match Align::from_bytes(align) {
|
||||
Ok(a) => a,
|
||||
Err(err) => throw_ub_custom!(
|
||||
fluent::const_eval_invalid_align_details,
|
||||
inline_fluent!(
|
||||
"invalid align passed to `{$name}`: {$align} is {$err_kind ->
|
||||
[not_power_of_two] not a power of 2
|
||||
[too_large] too large
|
||||
*[other] {\"\"}
|
||||
}"
|
||||
),
|
||||
name = "const_deallocate",
|
||||
err_kind = err.diag_ident(),
|
||||
align = err.align()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
use rustc_abi::FieldIdx;
|
||||
mod adt;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::span_bug;
|
||||
|
|
@ -8,11 +12,58 @@ use rustc_span::{Symbol, sym};
|
|||
|
||||
use crate::const_eval::CompileTimeMachine;
|
||||
use crate::interpret::{
|
||||
CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Scalar, Writeable,
|
||||
interp_ok,
|
||||
CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Projectable, Scalar,
|
||||
Writeable, interp_ok,
|
||||
};
|
||||
|
||||
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
||||
/// Equivalent to `project_downcast`, but identifies the variant by name instead of index.
|
||||
fn downcast<'a>(
|
||||
&self,
|
||||
place: &(impl Writeable<'tcx, CtfeProvenance> + 'a),
|
||||
name: Symbol,
|
||||
) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'a)> {
|
||||
let variants = place.layout().ty.ty_adt_def().unwrap().variants();
|
||||
let variant_idx = variants
|
||||
.iter_enumerated()
|
||||
.find(|(_idx, var)| var.name == name)
|
||||
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
|
||||
.0;
|
||||
|
||||
interp_ok((variant_idx, self.project_downcast(place, variant_idx)?))
|
||||
}
|
||||
|
||||
// A general method to write an array to a static slice place.
|
||||
fn allocate_fill_and_write_slice_ptr(
|
||||
&mut self,
|
||||
slice_place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
len: u64,
|
||||
writer: impl Fn(&mut Self, /* index */ u64, MPlaceTy<'tcx>) -> InterpResult<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Array element type
|
||||
let field_ty = slice_place
|
||||
.layout()
|
||||
.ty
|
||||
.builtin_deref(false)
|
||||
.unwrap()
|
||||
.sequence_element_type(self.tcx.tcx);
|
||||
|
||||
// Allocate an array
|
||||
let array_layout = self.layout_of(Ty::new_array(self.tcx.tcx, field_ty, len))?;
|
||||
let array_place = self.allocate(array_layout, MemoryKind::Stack)?;
|
||||
|
||||
// Fill the array fields
|
||||
let mut field_places = self.project_array_fields(&array_place)?;
|
||||
while let Some((i, place)) = field_places.next(self)? {
|
||||
writer(self, i, place)?;
|
||||
}
|
||||
|
||||
// Write the slice pointing to the array
|
||||
let array_place = array_place.map_provenance(CtfeProvenance::as_immutable);
|
||||
let ptr = Immediate::new_slice(array_place.ptr(), len, self);
|
||||
self.write_immediate(ptr, &slice_place)
|
||||
}
|
||||
|
||||
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
|
||||
pub(crate) fn write_type_info(
|
||||
&mut self,
|
||||
|
|
@ -26,22 +77,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
// Fill all fields of the `TypeInfo` struct.
|
||||
for (idx, field) in ty_struct.fields.iter_enumerated() {
|
||||
let field_dest = self.project_field(dest, idx)?;
|
||||
let downcast = |name: Symbol| {
|
||||
let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants();
|
||||
let variant_id = variants
|
||||
.iter_enumerated()
|
||||
.find(|(_idx, var)| var.name == name)
|
||||
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
|
||||
.0;
|
||||
|
||||
interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?))
|
||||
};
|
||||
let ptr_bit_width = || self.tcx.data_layout.pointer_size().bits();
|
||||
match field.name {
|
||||
sym::kind => {
|
||||
let variant_index = match ty.kind() {
|
||||
ty::Tuple(fields) => {
|
||||
let (variant, variant_place) = downcast(sym::Tuple)?;
|
||||
let (variant, variant_place) =
|
||||
self.downcast(&field_dest, sym::Tuple)?;
|
||||
// project to the single tuple variant field of `type_info::Tuple` struct type
|
||||
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
assert_eq!(
|
||||
|
|
@ -55,11 +97,12 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
.fields
|
||||
.len()
|
||||
);
|
||||
self.write_tuple_fields(tuple_place, fields, ty)?;
|
||||
self.write_tuple_type_info(tuple_place, fields, ty)?;
|
||||
variant
|
||||
}
|
||||
ty::Array(ty, len) => {
|
||||
let (variant, variant_place) = downcast(sym::Array)?;
|
||||
let (variant, variant_place) =
|
||||
self.downcast(&field_dest, sym::Array)?;
|
||||
let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
|
||||
self.write_array_type_info(array_place, *ty, *len)?;
|
||||
|
|
@ -67,23 +110,29 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
variant
|
||||
}
|
||||
ty::Slice(ty) => {
|
||||
let (variant, variant_place) = downcast(sym::Slice)?;
|
||||
let (variant, variant_place) =
|
||||
self.downcast(&field_dest, sym::Slice)?;
|
||||
let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
|
||||
self.write_slice_type_info(slice_place, *ty)?;
|
||||
|
||||
variant
|
||||
}
|
||||
ty::Adt(adt_def, generics) => {
|
||||
self.write_adt_type_info(&field_dest, (ty, *adt_def), generics)?
|
||||
}
|
||||
ty::Bool => {
|
||||
let (variant, _variant_place) = downcast(sym::Bool)?;
|
||||
let (variant, _variant_place) =
|
||||
self.downcast(&field_dest, sym::Bool)?;
|
||||
variant
|
||||
}
|
||||
ty::Char => {
|
||||
let (variant, _variant_place) = downcast(sym::Char)?;
|
||||
let (variant, _variant_place) =
|
||||
self.downcast(&field_dest, sym::Char)?;
|
||||
variant
|
||||
}
|
||||
ty::Int(int_ty) => {
|
||||
let (variant, variant_place) = downcast(sym::Int)?;
|
||||
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
|
||||
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_int_type_info(
|
||||
place,
|
||||
|
|
@ -93,7 +142,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
variant
|
||||
}
|
||||
ty::Uint(uint_ty) => {
|
||||
let (variant, variant_place) = downcast(sym::Int)?;
|
||||
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
|
||||
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_int_type_info(
|
||||
place,
|
||||
|
|
@ -103,17 +152,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
variant
|
||||
}
|
||||
ty::Float(float_ty) => {
|
||||
let (variant, variant_place) = downcast(sym::Float)?;
|
||||
let (variant, variant_place) =
|
||||
self.downcast(&field_dest, sym::Float)?;
|
||||
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_float_type_info(place, float_ty.bit_width())?;
|
||||
variant
|
||||
}
|
||||
ty::Str => {
|
||||
let (variant, _variant_place) = downcast(sym::Str)?;
|
||||
let (variant, _variant_place) = self.downcast(&field_dest, sym::Str)?;
|
||||
variant
|
||||
}
|
||||
ty::Ref(_, ty, mutability) => {
|
||||
let (variant, variant_place) = downcast(sym::Reference)?;
|
||||
let (variant, variant_place) =
|
||||
self.downcast(&field_dest, sym::Reference)?;
|
||||
let reference_place =
|
||||
self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_reference_type_info(reference_place, *ty, *mutability)?;
|
||||
|
|
@ -121,7 +172,8 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
variant
|
||||
}
|
||||
ty::RawPtr(ty, mutability) => {
|
||||
let (variant, variant_place) = downcast(sym::Pointer)?;
|
||||
let (variant, variant_place) =
|
||||
self.downcast(&field_dest, sym::Pointer)?;
|
||||
let pointer_place =
|
||||
self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
|
||||
|
|
@ -130,13 +182,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
variant
|
||||
}
|
||||
ty::Dynamic(predicates, region) => {
|
||||
let (variant, variant_place) = downcast(sym::DynTrait)?;
|
||||
let (variant, variant_place) =
|
||||
self.downcast(&field_dest, sym::DynTrait)?;
|
||||
let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
|
||||
variant
|
||||
}
|
||||
ty::Adt(_, _)
|
||||
| ty::Foreign(_)
|
||||
ty::Foreign(_)
|
||||
| ty::Pat(_, _)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
|
|
@ -151,14 +203,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
| ty::Bound(..)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Infer(..)
|
||||
| ty::Error(_) => downcast(sym::Other)?.0,
|
||||
| ty::Error(_) => self.downcast(&field_dest, sym::Other)?.0,
|
||||
};
|
||||
self.write_discriminant(variant_index, &field_dest)?
|
||||
}
|
||||
sym::size => {
|
||||
let layout = self.layout_of(ty)?;
|
||||
let variant_index = if layout.is_sized() {
|
||||
let (variant, variant_place) = downcast(sym::Some)?;
|
||||
let (variant, variant_place) = self.downcast(&field_dest, sym::Some)?;
|
||||
let size_field_place =
|
||||
self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_scalar(
|
||||
|
|
@ -168,7 +220,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
)?;
|
||||
variant
|
||||
} else {
|
||||
downcast(sym::None)?.0
|
||||
self.downcast(&field_dest, sym::None)?.0
|
||||
};
|
||||
self.write_discriminant(variant_index, &field_dest)?;
|
||||
}
|
||||
|
|
@ -179,46 +231,12 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn write_tuple_fields(
|
||||
&mut self,
|
||||
tuple_place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
fields: &[Ty<'tcx>],
|
||||
tuple_ty: Ty<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// project into the `type_info::Tuple::fields` field
|
||||
let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?;
|
||||
// get the `type_info::Field` type from `fields: &[Field]`
|
||||
let field_type = fields_slice_place
|
||||
.layout()
|
||||
.ty
|
||||
.builtin_deref(false)
|
||||
.unwrap()
|
||||
.sequence_element_type(self.tcx.tcx);
|
||||
// Create an array with as many elements as the number of fields in the inspected tuple
|
||||
let fields_layout =
|
||||
self.layout_of(Ty::new_array(self.tcx.tcx, field_type, fields.len() as u64))?;
|
||||
let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?;
|
||||
let mut fields_places = self.project_array_fields(&fields_place)?;
|
||||
|
||||
let tuple_layout = self.layout_of(tuple_ty)?;
|
||||
|
||||
while let Some((i, place)) = fields_places.next(self)? {
|
||||
let field_ty = fields[i as usize];
|
||||
self.write_field(field_ty, place, tuple_layout, i)?;
|
||||
}
|
||||
|
||||
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
|
||||
|
||||
let ptr = Immediate::new_slice(fields_place.ptr(), fields.len() as u64, self);
|
||||
|
||||
self.write_immediate(ptr, &fields_slice_place)
|
||||
}
|
||||
|
||||
fn write_field(
|
||||
&mut self,
|
||||
field_ty: Ty<'tcx>,
|
||||
place: MPlaceTy<'tcx>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
name: Option<Symbol>,
|
||||
idx: u64,
|
||||
) -> InterpResult<'tcx> {
|
||||
for (field_idx, field_ty_field) in
|
||||
|
|
@ -226,7 +244,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
{
|
||||
let field_place = self.project_field(&place, field_idx)?;
|
||||
match field_ty_field.name {
|
||||
sym::ty => self.write_type_id(field_ty, &field_place)?,
|
||||
sym::name => {
|
||||
let name = match name.as_ref() {
|
||||
Some(name) => Cow::Borrowed(name.as_str()),
|
||||
None => Cow::Owned(idx.to_string()), // For tuples
|
||||
};
|
||||
let name_place = self.allocate_str_dedup(&name)?;
|
||||
let ptr = self.mplace_to_ref(&name_place)?;
|
||||
self.write_immediate(*ptr, &field_place)?
|
||||
}
|
||||
sym::ty => {
|
||||
let field_ty = self.tcx.erase_and_anonymize_regions(field_ty);
|
||||
self.write_type_id(field_ty, &field_place)?
|
||||
}
|
||||
sym::offset => {
|
||||
let offset = layout.fields.offset(idx as usize);
|
||||
self.write_scalar(
|
||||
|
|
@ -242,6 +272,24 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn write_tuple_type_info(
|
||||
&mut self,
|
||||
tuple_place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
fields: &[Ty<'tcx>],
|
||||
tuple_ty: Ty<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let tuple_layout = self.layout_of(tuple_ty)?;
|
||||
let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?;
|
||||
self.allocate_fill_and_write_slice_ptr(
|
||||
fields_slice_place,
|
||||
fields.len() as u64,
|
||||
|this, i, place| {
|
||||
let field_ty = fields[i as usize];
|
||||
this.write_field(field_ty, place, tuple_layout, None, i)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn write_array_type_info(
|
||||
&mut self,
|
||||
place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
|
|
|
|||
276
compiler/rustc_const_eval/src/const_eval/type_info/adt.rs
Normal file
276
compiler/rustc_const_eval/src/const_eval/type_info/adt.rs
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{
|
||||
AdtDef, AdtKind, Const, ConstKind, GenericArgKind, GenericArgs, Region, Ty, VariantDef,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::sym;
|
||||
|
||||
use crate::const_eval::CompileTimeMachine;
|
||||
use crate::interpret::{
|
||||
CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Projectable, Scalar, Writeable, interp_ok,
|
||||
};
|
||||
|
||||
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
|
||||
// FIXME(type_info): No semver considerations for now
|
||||
pub(crate) fn write_adt_type_info(
|
||||
&mut self,
|
||||
place: &impl Writeable<'tcx, CtfeProvenance>,
|
||||
adt: (Ty<'tcx>, AdtDef<'tcx>),
|
||||
generics: &'tcx GenericArgs<'tcx>,
|
||||
) -> InterpResult<'tcx, VariantIdx> {
|
||||
let (adt_ty, adt_def) = adt;
|
||||
let variant_idx = match adt_def.adt_kind() {
|
||||
AdtKind::Struct => {
|
||||
let (variant, variant_place) = self.downcast(place, sym::Struct)?;
|
||||
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_struct_type_info(
|
||||
place,
|
||||
(adt_ty, adt_def.variant(VariantIdx::ZERO)),
|
||||
generics,
|
||||
)?;
|
||||
variant
|
||||
}
|
||||
AdtKind::Union => {
|
||||
let (variant, variant_place) = self.downcast(place, sym::Union)?;
|
||||
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_union_type_info(
|
||||
place,
|
||||
(adt_ty, adt_def.variant(VariantIdx::ZERO)),
|
||||
generics,
|
||||
)?;
|
||||
variant
|
||||
}
|
||||
AdtKind::Enum => {
|
||||
let (variant, variant_place) = self.downcast(place, sym::Enum)?;
|
||||
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
self.write_enum_type_info(place, adt, generics)?;
|
||||
variant
|
||||
}
|
||||
};
|
||||
interp_ok(variant_idx)
|
||||
}
|
||||
|
||||
pub(crate) fn write_struct_type_info(
|
||||
&mut self,
|
||||
place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
struct_: (Ty<'tcx>, &'tcx VariantDef),
|
||||
generics: &'tcx GenericArgs<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let (struct_ty, struct_def) = struct_;
|
||||
let struct_layout = self.layout_of(struct_ty)?;
|
||||
|
||||
for (field_idx, field) in
|
||||
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
|
||||
{
|
||||
let field_place = self.project_field(&place, field_idx)?;
|
||||
|
||||
match field.name {
|
||||
sym::generics => self.write_generics(field_place, generics)?,
|
||||
sym::fields => {
|
||||
self.write_variant_fields(field_place, struct_def, struct_layout, generics)?
|
||||
}
|
||||
sym::non_exhaustive => {
|
||||
let is_non_exhaustive = struct_def.is_field_list_non_exhaustive();
|
||||
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
|
||||
}
|
||||
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn write_union_type_info(
|
||||
&mut self,
|
||||
place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
union_: (Ty<'tcx>, &'tcx VariantDef),
|
||||
generics: &'tcx GenericArgs<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let (union_ty, union_def) = union_;
|
||||
let union_layout = self.layout_of(union_ty)?;
|
||||
|
||||
for (field_idx, field) in
|
||||
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
|
||||
{
|
||||
let field_place = self.project_field(&place, field_idx)?;
|
||||
|
||||
match field.name {
|
||||
sym::generics => self.write_generics(field_place, generics)?,
|
||||
sym::fields => {
|
||||
self.write_variant_fields(field_place, union_def, union_layout, generics)?
|
||||
}
|
||||
sym::non_exhaustive => {
|
||||
let is_non_exhaustive = union_def.is_field_list_non_exhaustive();
|
||||
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
|
||||
}
|
||||
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn write_enum_type_info(
|
||||
&mut self,
|
||||
place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
enum_: (Ty<'tcx>, AdtDef<'tcx>),
|
||||
generics: &'tcx GenericArgs<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let (enum_ty, enum_def) = enum_;
|
||||
let enum_layout = self.layout_of(enum_ty)?;
|
||||
|
||||
for (field_idx, field) in
|
||||
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
|
||||
{
|
||||
let field_place = self.project_field(&place, field_idx)?;
|
||||
|
||||
match field.name {
|
||||
sym::generics => self.write_generics(field_place, generics)?,
|
||||
sym::variants => {
|
||||
self.allocate_fill_and_write_slice_ptr(
|
||||
field_place,
|
||||
enum_def.variants().len() as u64,
|
||||
|this, i, place| {
|
||||
let variant_idx = VariantIdx::from_usize(i as usize);
|
||||
let variant_def = &enum_def.variants()[variant_idx];
|
||||
let variant_layout = enum_layout.for_variant(this, variant_idx);
|
||||
this.write_enum_variant(place, (variant_layout, &variant_def), generics)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
sym::non_exhaustive => {
|
||||
let is_non_exhaustive = enum_def.is_variant_list_non_exhaustive();
|
||||
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
|
||||
}
|
||||
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn write_enum_variant(
|
||||
&mut self,
|
||||
place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
variant: (TyAndLayout<'tcx>, &'tcx VariantDef),
|
||||
generics: &'tcx GenericArgs<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let (variant_layout, variant_def) = variant;
|
||||
|
||||
for (field_idx, field_def) in
|
||||
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
|
||||
{
|
||||
let field_place = self.project_field(&place, field_idx)?;
|
||||
match field_def.name {
|
||||
sym::name => {
|
||||
let name_place = self.allocate_str_dedup(variant_def.name.as_str())?;
|
||||
let ptr = self.mplace_to_ref(&name_place)?;
|
||||
self.write_immediate(*ptr, &field_place)?
|
||||
}
|
||||
sym::fields => {
|
||||
self.write_variant_fields(field_place, &variant_def, variant_layout, generics)?
|
||||
}
|
||||
sym::non_exhaustive => {
|
||||
let is_non_exhaustive = variant_def.is_field_list_non_exhaustive();
|
||||
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
|
||||
}
|
||||
other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
|
||||
}
|
||||
}
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
// Write fields for struct, enum variants
|
||||
fn write_variant_fields(
|
||||
&mut self,
|
||||
place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
variant_def: &'tcx VariantDef,
|
||||
variant_layout: TyAndLayout<'tcx>,
|
||||
generics: &'tcx GenericArgs<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.allocate_fill_and_write_slice_ptr(
|
||||
place,
|
||||
variant_def.fields.len() as u64,
|
||||
|this, i, place| {
|
||||
let field_def = &variant_def.fields[FieldIdx::from_usize(i as usize)];
|
||||
let field_ty = field_def.ty(*this.tcx, generics);
|
||||
this.write_field(field_ty, place, variant_layout, Some(field_def.name), i)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn write_generics(
|
||||
&mut self,
|
||||
place: impl Writeable<'tcx, CtfeProvenance>,
|
||||
generics: &'tcx GenericArgs<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.allocate_fill_and_write_slice_ptr(place, generics.len() as u64, |this, i, place| {
|
||||
match generics[i as usize].kind() {
|
||||
GenericArgKind::Lifetime(region) => this.write_generic_lifetime(region, place),
|
||||
GenericArgKind::Type(ty) => this.write_generic_type(ty, place),
|
||||
GenericArgKind::Const(c) => this.write_generic_const(c, place),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn write_generic_lifetime(
|
||||
&mut self,
|
||||
_region: Region<'tcx>,
|
||||
place: MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let (variant_idx, _) = self.downcast(&place, sym::Lifetime)?;
|
||||
self.write_discriminant(variant_idx, &place)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn write_generic_type(&mut self, ty: Ty<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
|
||||
let (variant_idx, variant_place) = self.downcast(&place, sym::Type)?;
|
||||
let generic_type_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
|
||||
for (field_idx, field_def) in generic_type_place
|
||||
.layout()
|
||||
.ty
|
||||
.ty_adt_def()
|
||||
.unwrap()
|
||||
.non_enum_variant()
|
||||
.fields
|
||||
.iter_enumerated()
|
||||
{
|
||||
let field_place = self.project_field(&generic_type_place, field_idx)?;
|
||||
match field_def.name {
|
||||
sym::ty => self.write_type_id(ty, &field_place)?,
|
||||
other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
self.write_discriminant(variant_idx, &place)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn write_generic_const(&mut self, c: Const<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
|
||||
let ConstKind::Value(c) = c.kind() else { bug!("expected a computed const, got {c:?}") };
|
||||
|
||||
let (variant_idx, variant_place) = self.downcast(&place, sym::Const)?;
|
||||
let const_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
|
||||
|
||||
for (field_idx, field_def) in const_place
|
||||
.layout()
|
||||
.ty
|
||||
.ty_adt_def()
|
||||
.unwrap()
|
||||
.non_enum_variant()
|
||||
.fields
|
||||
.iter_enumerated()
|
||||
{
|
||||
let field_place = self.project_field(&const_place, field_idx)?;
|
||||
match field_def.name {
|
||||
sym::ty => self.write_type_id(c.ty, &field_place)?,
|
||||
other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
self.write_discriminant(variant_idx, &place)?;
|
||||
interp_ok(())
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,12 @@
|
|||
//! Manages calling a concrete function (with known MIR body) with argument passing,
|
||||
//! and returning the return value to the caller.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use either::{Left, Right};
|
||||
use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
|
||||
use rustc_data_structures::assert_matches;
|
||||
use rustc_errors::inline_fluent;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
|
||||
use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
|
||||
|
|
@ -19,8 +21,8 @@ use super::{
|
|||
Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok,
|
||||
throw_ub, throw_ub_custom, throw_unsup_format,
|
||||
};
|
||||
use crate::enter_trace_span;
|
||||
use crate::interpret::EnteredTraceSpan;
|
||||
use crate::{enter_trace_span, fluent_generated as fluent};
|
||||
|
||||
/// An argument passed to a function.
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -89,6 +91,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let (_, field) = layout.non_1zst_field(self).unwrap();
|
||||
self.unfold_transparent(field, may_unfold)
|
||||
}
|
||||
ty::Pat(base, _) => self.layout_of(*base).expect(
|
||||
"if the layout of a pattern type could be computed, so can the layout of its base",
|
||||
),
|
||||
// Not a transparent type, no further unfolding.
|
||||
_ => layout,
|
||||
}
|
||||
|
|
@ -292,7 +297,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
// Find next caller arg.
|
||||
let Some((caller_arg, caller_abi)) = caller_args.next() else {
|
||||
throw_ub_custom!(fluent::const_eval_not_enough_caller_args);
|
||||
throw_ub_custom!(inline_fluent!(
|
||||
"calling a function with fewer arguments than it requires"
|
||||
));
|
||||
};
|
||||
assert_eq!(caller_arg.layout().layout, caller_abi.layout.layout);
|
||||
// Sadly we cannot assert that `caller_arg.layout().ty` and `caller_abi.layout.ty` are
|
||||
|
|
@ -359,7 +366,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
if caller_fn_abi.conv != callee_fn_abi.conv {
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_incompatible_calling_conventions,
|
||||
rustc_errors::inline_fluent!(
|
||||
"calling a function with calling convention \"{$callee_conv}\" using calling convention \"{$caller_conv}\""
|
||||
),
|
||||
callee_conv = format!("{}", callee_fn_abi.conv),
|
||||
caller_conv = format!("{}", caller_fn_abi.conv),
|
||||
)
|
||||
|
|
@ -490,7 +499,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
"mismatch between callee ABI and callee body arguments"
|
||||
);
|
||||
if caller_args.next().is_some() {
|
||||
throw_ub_custom!(fluent::const_eval_too_many_caller_args);
|
||||
throw_ub_custom!(inline_fluent!(
|
||||
"calling a function with more arguments than it expected"
|
||||
));
|
||||
}
|
||||
// Don't forget to check the return type!
|
||||
if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
|
||||
|
|
@ -690,7 +701,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let vtable_entries = self.vtable_entries(receiver_trait.principal(), dyn_ty);
|
||||
let Some(ty::VtblEntry::Method(fn_inst)) = vtable_entries.get(idx).copied() else {
|
||||
// FIXME(fee1-dead) these could be variants of the UB info enum instead of this
|
||||
throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method);
|
||||
throw_ub_custom!(inline_fluent!(
|
||||
"`dyn` call trying to call something that is not a method"
|
||||
));
|
||||
};
|
||||
trace!("Virtual call dispatches to {fn_inst:#?}");
|
||||
// We can also do the lookup based on `def_id` and `dyn_ty`, and check that that
|
||||
|
|
@ -887,7 +900,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
);
|
||||
if unwinding && self.frame_idx() == 0 {
|
||||
throw_ub_custom!(fluent::const_eval_unwind_past_top);
|
||||
throw_ub_custom!(inline_fluent!("unwinding past the topmost frame of the stack"));
|
||||
}
|
||||
|
||||
// Get out the return value. Must happen *before* the frame is popped as we have to get the
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use rustc_abi::{FieldIdx, Integer};
|
|||
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
|
||||
use rustc_apfloat::{Float, FloatConvert};
|
||||
use rustc_data_structures::assert_matches;
|
||||
use rustc_errors::inline_fluent;
|
||||
use rustc_middle::mir::CastKind;
|
||||
use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
|
|
@ -15,8 +16,8 @@ use super::{
|
|||
FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, err_inval, interp_ok, throw_ub,
|
||||
throw_ub_custom,
|
||||
};
|
||||
use crate::enter_trace_span;
|
||||
use crate::interpret::Writeable;
|
||||
use crate::{enter_trace_span, fluent_generated as fluent};
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
pub fn cast(
|
||||
|
|
@ -138,7 +139,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
assert_eq!(cast_ty, dest.layout.ty); // we otherwise ignore `cast_ty` enirely...
|
||||
if src.layout.size != dest.layout.size {
|
||||
throw_ub_custom!(
|
||||
fluent::const_eval_invalid_transmute,
|
||||
inline_fluent!(
|
||||
"transmuting from {$src_bytes}-byte type to {$dest_bytes}-byte type: `{$src}` -> `{$dest}`"
|
||||
),
|
||||
src_bytes = src.layout.size.bytes(),
|
||||
dest_bytes = dest.layout.size.bytes(),
|
||||
src = src.layout.ty,
|
||||
|
|
|
|||
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