Merge ref '4c7749e8c8' from rust-lang/rust

Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: 4c7749e8c8
Filtered ref: 9742b5940dcb4aea810f9ce020a92b3f72ed7bb4

This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
The Miri Cronjob Bot 2025-08-09 05:04:41 +00:00
commit 89ed378953
1237 changed files with 19128 additions and 15199 deletions

2
.gitmodules vendored
View file

@ -25,7 +25,7 @@
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
branch = rustc/20.1-2025-07-13
branch = rustc/21.1-2025-08-01
shallow = true
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book

View file

@ -568,7 +568,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]]
name = "clippy"
version = "0.1.90"
version = "0.1.91"
dependencies = [
"anstream",
"askama",
@ -595,7 +595,7 @@ dependencies = [
[[package]]
name = "clippy_config"
version = "0.1.90"
version = "0.1.91"
dependencies = [
"clippy_utils",
"itertools",
@ -618,7 +618,7 @@ dependencies = [
[[package]]
name = "clippy_lints"
version = "0.1.90"
version = "0.1.91"
dependencies = [
"arrayvec",
"cargo_metadata 0.18.1",
@ -649,7 +649,7 @@ dependencies = [
[[package]]
name = "clippy_utils"
version = "0.1.90"
version = "0.1.91"
dependencies = [
"arrayvec",
"itertools",
@ -1051,7 +1051,7 @@ dependencies = [
[[package]]
name = "declare_clippy_lint"
version = "0.1.90"
version = "0.1.91"
[[package]]
name = "derive-where"

View file

@ -1,3 +1,140 @@
Version 1.89.0 (2025-08-07)
==========================
<a id="1.89.0-Language"></a>
Language
--------
- [Stabilize explicitly inferred const arguments (`feature(generic_arg_infer)`)](https://github.com/rust-lang/rust/pull/141610)
- [Add a warn-by-default `mismatched_lifetime_syntaxes` lint.](https://github.com/rust-lang/rust/pull/138677)
This lint detects when the same lifetime is referred to by different syntax categories between function arguments and return values, which can be confusing to read, especially in unsafe code.
This lint supersedes the warn-by-default `elided_named_lifetimes` lint.
- [Expand `unpredictable_function_pointer_comparisons` to also lint on function pointer comparisons in external macros](https://github.com/rust-lang/rust/pull/134536)
- [Make the `dangerous_implicit_autorefs` lint deny-by-default](https://github.com/rust-lang/rust/pull/141661)
- [Stabilize the avx512 target features](https://github.com/rust-lang/rust/pull/138940)
- [Stabilize `kl` and `widekl` target features for x86](https://github.com/rust-lang/rust/pull/140766)
- [Stabilize `sha512`, `sm3` and `sm4` target features for x86](https://github.com/rust-lang/rust/pull/140767)
- [Stabilize LoongArch target features `f`, `d`, `frecipe`, `lasx`, `lbt`, `lsx`, and `lvz`](https://github.com/rust-lang/rust/pull/135015)
- [Remove `i128` and `u128` from `improper_ctypes_definitions`](https://github.com/rust-lang/rust/pull/137306)
- [Stabilize `repr128` (`#[repr(u128)]`, `#[repr(i128)]`)](https://github.com/rust-lang/rust/pull/138285)
- [Allow `#![doc(test(attr(..)))]` everywhere](https://github.com/rust-lang/rust/pull/140560)
- [Extend temporary lifetime extension to also go through tuple struct and tuple variant constructors](https://github.com/rust-lang/rust/pull/140593)
- [`extern "C"` functions on the `wasm32-unknown-unknown` target now have a standards compliant ABI](https://blog.rust-lang.org/2025/04/04/c-abi-changes-for-wasm32-unknown-unknown/)
<a id="1.89.0-Compiler"></a>
Compiler
--------
- [Default to non-leaf frame pointers on aarch64-linux](https://github.com/rust-lang/rust/pull/140832)
- [Enable non-leaf frame pointers for Arm64EC Windows](https://github.com/rust-lang/rust/pull/140862)
- [Set Apple frame pointers by architecture](https://github.com/rust-lang/rust/pull/141797)
<a id="1.89.0-Platform-Support"></a>
Platform Support
----------------
- [Add new Tier-3 targets `loongarch32-unknown-none` and `loongarch32-unknown-none-softfloat`](https://github.com/rust-lang/rust/pull/142053)
- [`x86_64-apple-darwin` is in the process of being demoted to Tier 2 with host tools](https://github.com/rust-lang/rfcs/pull/3841)
Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.
[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html
<a id="1.89.0-Libraries"></a>
Libraries
---------
- [Specify the base path for `file!`](https://github.com/rust-lang/rust/pull/134442)
- [Allow storing `format_args!()` in a variable](https://github.com/rust-lang/rust/pull/140748)
- [Add `#[must_use]` to `[T; N]::map`](https://github.com/rust-lang/rust/pull/140957)
- [Implement `DerefMut` for `Lazy{Cell,Lock}`](https://github.com/rust-lang/rust/pull/129334)
- [Implement `Default` for `array::IntoIter`](https://github.com/rust-lang/rust/pull/141574)
- [Implement `Clone` for `slice::ChunkBy`](https://github.com/rust-lang/rust/pull/138016)
- [Implement `io::Seek` for `io::Take`](https://github.com/rust-lang/rust/pull/138023)
<a id="1.89.0-Stabilized-APIs"></a>
Stabilized APIs
---------------
- [`NonZero<char>`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html)
- Many intrinsics for x86, not enumerated here
- [AVX512 intrinsics](https://github.com/rust-lang/rust/issues/111137)
- [`SHA512`, `SM3` and `SM4` intrinsics](https://github.com/rust-lang/rust/issues/126624)
- [`File::lock`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.lock)
- [`File::lock_shared`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.lock_shared)
- [`File::try_lock`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.try_lock)
- [`File::try_lock_shared`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.try_lock_shared)
- [`File::unlock`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.unlock)
- [`NonNull::from_ref`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.from_ref)
- [`NonNull::from_mut`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.from_mut)
- [`NonNull::without_provenance`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.without_provenance)
- [`NonNull::with_exposed_provenance`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.with_exposed_provenance)
- [`NonNull::expose_provenance`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.expose_provenance)
- [`OsString::leak`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.leak)
- [`PathBuf::leak`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.leak)
- [`Result::flatten`](https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.flatten)
- [`std::os::linux::net::TcpStreamExt::quickack`](https://doc.rust-lang.org/stable/std/os/linux/net/trait.TcpStreamExt.html#tymethod.quickack)
- [`std::os::linux::net::TcpStreamExt::set_quickack`](https://doc.rust-lang.org/stable/std/os/linux/net/trait.TcpStreamExt.html#tymethod.set_quickack)
These previously stable APIs are now stable in const contexts:
- [`<[T; N]>::as_mut_slice`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.as_mut_slice)
- [`<[u8]>::eq_ignore_ascii_case`](https://doc.rust-lang.org/stable/std/primitive.slice.html#impl-%5Bu8%5D/method.eq_ignore_ascii_case)
- [`str::eq_ignore_ascii_case`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-str/method.eq_ignore_ascii_case)
<a id="1.89.0-Cargo"></a>
Cargo
-----
- [`cargo fix` and `cargo clippy --fix` now default to the same Cargo target selection as other build commands.](https://github.com/rust-lang/cargo/pull/15192/) Previously it would apply to all targets (like binaries, examples, tests, etc.). The `--edition` flag still applies to all targets.
- [Stabilize doctest-xcompile.](https://github.com/rust-lang/cargo/pull/15462/) Doctests are now tested when cross-compiling. Just like other tests, it will use the [`runner` setting](https://doc.rust-lang.org/cargo/reference/config.html#targettriplerunner) to run the tests. If you need to disable tests for a target, you can use the [ignore doctest attribute](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html#ignoring-targets) to specify the targets to ignore.
<a id="1.89.0-Rustdoc"></a>
Rustdoc
-----
- [On mobile, make the sidebar full width and linewrap](https://github.com/rust-lang/rust/pull/139831). This makes long section and item names much easier to deal with on mobile.
<a id="1.89.0-Compatibility-Notes"></a>
Compatibility Notes
-------------------
- [Make `missing_fragment_specifier` an unconditional error](https://github.com/rust-lang/rust/pull/128425)
- [Enabling the `neon` target feature on `aarch64-unknown-none-softfloat` causes a warning](https://github.com/rust-lang/rust/pull/135160) because mixing code with and without that target feature is not properly supported by LLVM
- [Sized Hierarchy: Part I](https://github.com/rust-lang/rust/pull/137944)
- Introduces a small breaking change affecting `?Sized` bounds on impls on recursive types which contain associated type projections. It is not expected to affect any existing published crates. Can be fixed by refactoring the involved types or opting into the `sized_hierarchy` unstable feature. See the [FCP report](https://github.com/rust-lang/rust/pull/137944#issuecomment-2912207485) for a code example.
- The warn-by-default `elided_named_lifetimes` lint is [superseded by the warn-by-default `mismatched_lifetime_syntaxes` lint.](https://github.com/rust-lang/rust/pull/138677)
- [Error on recursive opaque types earlier in the type checker](https://github.com/rust-lang/rust/pull/139419)
- [Type inference side effects from requiring element types of array repeat expressions are `Copy` are now only available at the end of type checking](https://github.com/rust-lang/rust/pull/139635)
- [The deprecated accidentally-stable `std::intrinsics::{copy,copy_nonoverlapping,write_bytes}` are now proper intrinsics](https://github.com/rust-lang/rust/pull/139916). There are no debug assertions guarding against UB, and they cannot be coerced to function pointers.
- [Remove long-deprecated `std::intrinsics::drop_in_place`](https://github.com/rust-lang/rust/pull/140151)
- [Make well-formedness predicates no longer coinductive](https://github.com/rust-lang/rust/pull/140208)
- [Remove hack when checking impl method compatibility](https://github.com/rust-lang/rust/pull/140557)
- [Remove unnecessary type inference due to built-in trait object impls](https://github.com/rust-lang/rust/pull/141352)
- [Lint against "stdcall", "fastcall", and "cdecl" on non-x86-32 targets](https://github.com/rust-lang/rust/pull/141435)
- [Future incompatibility warnings relating to the never type (`!`) are now reported in dependencies](https://github.com/rust-lang/rust/pull/141937)
- [Ensure `std::ptr::copy_*` intrinsics also perform the static self-init checks](https://github.com/rust-lang/rust/pull/142575)
- [`extern "C"` functions on the `wasm32-unknown-unknown` target now have a standards compliant ABI](https://blog.rust-lang.org/2025/04/04/c-abi-changes-for-wasm32-unknown-unknown/)
<a id="1.89.0-Internal-Changes"></a>
Internal Changes
----------------
These changes do not affect any public interfaces of Rust, but they represent
significant improvements to the performance or internals of rustc and related
tools.
- [Correctly un-remap compiler sources paths with the `rustc-dev` component](https://github.com/rust-lang/rust/pull/142377)
Version 1.88.0 (2025-06-26)
==========================

View file

@ -98,7 +98,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
let expr_hir_id = self.lower_node_id(e.id);
self.lower_attrs(expr_hir_id, &e.attrs, e.span);
let attrs = self.lower_attrs(expr_hir_id, &e.attrs, e.span);
let kind = match &e.kind {
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
@ -232,10 +232,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
*fn_arg_span,
),
None => self.lower_expr_closure(
attrs,
binder,
*capture_clause,
e.id,
expr_hir_id,
*constness,
*movability,
fn_decl,
@ -1052,10 +1052,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_expr_closure(
&mut self,
attrs: &[rustc_hir::Attribute],
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
closure_hir_id: hir::HirId,
constness: Const,
movability: Movability,
decl: &FnDecl,
@ -1067,15 +1067,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
let mut coroutine_kind = if this
.attrs
.get(&closure_hir_id.local_id)
.is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine)))
{
Some(hir::CoroutineKind::Coroutine(Movability::Movable))
} else {
None
};
let mut coroutine_kind = find_attr!(attrs, AttributeKind::Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable));
// FIXME(contracts): Support contracts on closures?
let body_id = this.lower_fn_body(decl, None, |this| {
this.coroutine_kind = coroutine_kind;

View file

@ -40,7 +40,7 @@ ast_passes_auto_generic = auto traits cannot have generic parameters
ast_passes_auto_items = auto traits cannot have associated items
.label = {ast_passes_auto_items}
.suggestion = remove these associated items
.suggestion = remove the associated items
ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetime bounds
.label = {ast_passes_auto_super_lifetime}

View file

@ -699,19 +699,23 @@ impl<'a> AstValidator<'a> {
}
}
fn deny_super_traits(&self, bounds: &GenericBounds, ident_span: Span) {
fn deny_super_traits(&self, bounds: &GenericBounds, ident: Span) {
if let [.., last] = &bounds[..] {
let span = ident_span.shrink_to_hi().to(last.span());
self.dcx().emit_err(errors::AutoTraitBounds { span, ident: ident_span });
let span = bounds.iter().map(|b| b.span()).collect();
let removal = ident.shrink_to_hi().to(last.span());
self.dcx().emit_err(errors::AutoTraitBounds { span, removal, ident });
}
}
fn deny_where_clause(&self, where_clause: &WhereClause, ident_span: Span) {
fn deny_where_clause(&self, where_clause: &WhereClause, ident: Span) {
if !where_clause.predicates.is_empty() {
// FIXME: The current diagnostic is misleading since it only talks about
// super trait and lifetime bounds while we should just say “bounds”.
self.dcx()
.emit_err(errors::AutoTraitBounds { span: where_clause.span, ident: ident_span });
self.dcx().emit_err(errors::AutoTraitBounds {
span: vec![where_clause.span],
removal: where_clause.span,
ident,
});
}
}

View file

@ -344,7 +344,7 @@ pub(crate) struct ModuleNonAscii {
#[diag(ast_passes_auto_generic, code = E0567)]
pub(crate) struct AutoTraitGeneric {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable")]
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub span: Span,
#[label]
pub ident: Span,
@ -354,8 +354,9 @@ pub(crate) struct AutoTraitGeneric {
#[diag(ast_passes_auto_super_lifetime, code = E0568)]
pub(crate) struct AutoTraitBounds {
#[primary_span]
#[suggestion(code = "", applicability = "machine-applicable")]
pub span: Span,
pub span: Vec<Span>,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub removal: Span,
#[label]
pub ident: Span,
}
@ -365,7 +366,7 @@ pub(crate) struct AutoTraitBounds {
pub(crate) struct AutoTraitItems {
#[primary_span]
pub spans: Vec<Span>,
#[suggestion(code = "", applicability = "machine-applicable")]
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub total: Span,
#[label]
pub ident: Span,

View file

@ -0,0 +1,15 @@
//! Attributes that can be found in function body.
use rustc_hir::attrs::AttributeKind;
use rustc_span::{Symbol, sym};
use super::{NoArgsAttributeParser, OnDuplicate};
use crate::context::Stage;
pub(crate) struct CoroutineParser;
impl<S: Stage> NoArgsAttributeParser<S> for CoroutineParser {
const PATH: &[Symbol] = &[sym::coroutine];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(rustc_span::Span) -> AttributeKind = |span| AttributeKind::Coroutine(span);
}

View file

@ -26,6 +26,7 @@ use crate::parser::ArgParser;
use crate::session_diagnostics::UnusedMultiple;
pub(crate) mod allow_unstable;
pub(crate) mod body;
pub(crate) mod cfg;
pub(crate) mod cfg_old;
pub(crate) mod codegen_attrs;

View file

@ -44,3 +44,55 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
})
}
}
pub(crate) struct ShouldPanicParser;
impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
const PATH: &[Symbol] = &[sym::should_panic];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate =
template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::ShouldPanic {
span: cx.attr_span,
reason: match args {
ArgParser::NoArgs => None,
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
cx.expected_string_literal(
name_value.value_span,
Some(name_value.value_as_lit()),
);
return None;
};
Some(str_value)
}
ArgParser::List(list) => {
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
let Some(single) = single.meta_item() else {
cx.expected_name_value(single.span(), Some(sym::expected));
return None;
};
if !single.path().word_is(sym::expected) {
cx.expected_specific_argument_strings(list.span, vec!["expected"]);
return None;
}
let Some(nv) = single.args().name_value() else {
cx.expected_name_value(single.span(), Some(sym::expected));
return None;
};
let Some(expected) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
Some(expected)
}
},
})
}
}

View file

@ -16,6 +16,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::allow_unstable::{
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
TargetFeatureParser, TrackCallerParser, UsedParser,
@ -49,7 +50,7 @@ use crate::attributes::semantics::MayDangleParser;
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
use crate::attributes::test_attrs::IgnoreParser;
use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser};
use crate::attributes::traits::{
AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser,
DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser,
@ -173,6 +174,7 @@ attribute_parsers!(
Single<RustcLayoutScalarValidRangeEnd>,
Single<RustcLayoutScalarValidRangeStart>,
Single<RustcObjectLifetimeDefaultParser>,
Single<ShouldPanicParser>,
Single<SkipDuringMethodDispatchParser>,
Single<TransparencyParser>,
Single<WithoutArgs<AllowIncoherentImplParser>>,
@ -184,6 +186,7 @@ attribute_parsers!(
Single<WithoutArgs<ConstContinueParser>>,
Single<WithoutArgs<ConstStabilityIndirectParser>>,
Single<WithoutArgs<ConstTraitParser>>,
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<ExportStableParser>>,

View file

@ -19,6 +19,7 @@ use std::borrow::Cow;
use std::cell::{OnceCell, RefCell};
use std::marker::PhantomData;
use std::ops::{ControlFlow, Deref};
use std::rc::Rc;
use borrow_set::LocalsStateAtExit;
use root_cx::BorrowCheckRootCtxt;
@ -44,6 +45,7 @@ use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces}
use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
@ -60,11 +62,14 @@ use crate::path_utils::*;
use crate::place_ext::PlaceExt;
use crate::places_conflict::{PlaceConflictBias, places_conflict};
use crate::polonius::PoloniusDiagnosticsContext;
use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput};
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
};
use crate::prefixes::PrefixSet;
use crate::region_infer::RegionInferenceContext;
use crate::renumber::RegionCtxt;
use crate::session_diagnostics::VarNeedNotMut;
use crate::type_check::MirTypeckResults;
mod borrow_set;
mod borrowck_errors;
@ -321,7 +326,34 @@ fn do_mir_borrowck<'tcx>(
let locals_are_invalidated_at_exit = tcx.hir_body_owner_kind(def).is_fn_or_closure();
let borrow_set = BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &move_data);
// Compute non-lexical lifetimes.
let location_map = Rc::new(DenseLocationMap::new(body));
let polonius_input = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_input())
|| infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
let mut polonius_facts =
(polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());
// Run the MIR type-checker.
let MirTypeckResults {
constraints,
universal_region_relations,
opaque_type_values,
polonius_context,
} = type_check::type_check(
root_cx,
&infcx,
body,
&promoted,
universal_regions,
&location_table,
&borrow_set,
&mut polonius_facts,
&move_data,
Rc::clone(&location_map),
);
// Compute non-lexical lifetimes using the constraints computed
// by typechecking the MIR body.
let nll::NllOutput {
regioncx,
polonius_input,
@ -332,14 +364,19 @@ fn do_mir_borrowck<'tcx>(
} = nll::compute_regions(
root_cx,
&infcx,
universal_regions,
body,
&promoted,
&location_table,
&move_data,
&borrow_set,
location_map,
universal_region_relations,
constraints,
polonius_facts,
polonius_context,
);
regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values);
// Dump MIR results into a file, if that is enabled. This lets us
// write unit-tests, as well as helping with debugging.
nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);

View file

@ -5,7 +5,8 @@ use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
use polonius_engine::{Algorithm, Output};
use polonius_engine::{Algorithm, AllFacts, Output};
use rustc_data_structures::frozen::Frozen;
use rustc_index::IndexSlice;
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
@ -18,14 +19,16 @@ 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::PoloniusDiagnosticsContext;
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
};
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
use crate::region_infer::RegionInferenceContext;
use crate::type_check::{self, MirTypeckResults};
use crate::type_check::MirTypeckRegionConstraints;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::universal_regions::UniversalRegions;
use crate::{
BorrowCheckRootCtxt, BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements,
@ -76,41 +79,18 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
pub(crate) fn compute_regions<'tcx>(
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
universal_regions: UniversalRegions<'tcx>,
body: &Body<'tcx>,
promoted: &IndexSlice<Promoted, Body<'tcx>>,
location_table: &PoloniusLocationTable,
move_data: &MoveData<'tcx>,
borrow_set: &BorrowSet<'tcx>,
location_map: Rc<DenseLocationMap>,
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
constraints: MirTypeckRegionConstraints<'tcx>,
mut polonius_facts: Option<AllFacts<RustcFacts>>,
polonius_context: Option<PoloniusContext>,
) -> NllOutput<'tcx> {
let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
let polonius_input = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_input())
|| is_polonius_legacy_enabled;
let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
|| is_polonius_legacy_enabled;
let mut polonius_facts =
(polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());
let location_map = Rc::new(DenseLocationMap::new(body));
// Run the MIR type-checker.
let MirTypeckResults {
constraints,
universal_region_relations,
opaque_type_values,
polonius_context,
} = type_check::type_check(
root_cx,
infcx,
body,
promoted,
universal_regions,
location_table,
borrow_set,
&mut polonius_facts,
move_data,
Rc::clone(&location_map),
);
|| infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
constraints,
@ -173,8 +153,6 @@ pub(crate) fn compute_regions<'tcx>(
infcx.set_tainted_by_errors(guar);
}
regioncx.infer_opaque_types(root_cx, infcx, opaque_type_values);
NllOutput {
regioncx,
polonius_input: polonius_facts.map(Box::new),

View file

@ -769,9 +769,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
TerminatorKind::Call { func, args, .. }
| TerminatorKind::TailCall { func, args, .. } => {
let call_source = match term.kind {
TerminatorKind::Call { call_source, .. } => call_source,
TerminatorKind::TailCall { .. } => CallSource::Normal,
let (call_source, destination, is_diverging) = match term.kind {
TerminatorKind::Call { call_source, destination, target, .. } => {
(call_source, destination, target.is_none())
}
TerminatorKind::TailCall { .. } => {
(CallSource::Normal, RETURN_PLACE.into(), false)
}
_ => unreachable!(),
};
@ -845,9 +849,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
if let TerminatorKind::Call { destination, target, .. } = term.kind {
self.check_call_dest(term, &sig, destination, target, term_location);
}
self.check_call_dest(term, &sig, destination, is_diverging, term_location);
// The ordinary liveness rules will ensure that all
// regions in the type of the callee are live here. We
@ -1874,65 +1876,61 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
term: &Terminator<'tcx>,
sig: &ty::FnSig<'tcx>,
destination: Place<'tcx>,
target: Option<BasicBlock>,
is_diverging: bool,
term_location: Location,
) {
let tcx = self.tcx();
match target {
Some(_) => {
let dest_ty = destination.ty(self.body, tcx).ty;
let dest_ty = self.normalize(dest_ty, term_location);
let category = match destination.as_local() {
Some(RETURN_PLACE) => {
if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
self.universal_regions.defining_ty
{
if tcx.is_static(def_id) {
ConstraintCategory::UseAsStatic
} else {
ConstraintCategory::UseAsConst
}
} else {
ConstraintCategory::Return(ReturnConstraint::Normal)
}
}
Some(l) if !self.body.local_decls[l].is_user_variable() => {
ConstraintCategory::Boring
}
// The return type of a call is interesting for diagnostics.
_ => ConstraintCategory::Assignment,
};
let locations = term_location.to_locations();
if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
span_mirbug!(
self,
term,
"call dest mismatch ({:?} <- {:?}): {:?}",
dest_ty,
sig.output(),
terr
);
}
// When `unsized_fn_params` is not enabled,
// this check is done at `check_local`.
if self.unsized_feature_enabled() {
let span = term.source_info.span;
self.ensure_place_sized(dest_ty, span);
}
if is_diverging {
// The signature in this call can reference region variables,
// so erase them before calling a query.
let output_ty = self.tcx().erase_regions(sig.output());
if !output_ty
.is_privately_uninhabited(self.tcx(), self.infcx.typing_env(self.infcx.param_env))
{
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
None => {
// The signature in this call can reference region variables,
// so erase them before calling a query.
let output_ty = self.tcx().erase_regions(sig.output());
if !output_ty.is_privately_uninhabited(
self.tcx(),
self.infcx.typing_env(self.infcx.param_env),
) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
} else {
let dest_ty = destination.ty(self.body, tcx).ty;
let dest_ty = self.normalize(dest_ty, term_location);
let category = match destination.as_local() {
Some(RETURN_PLACE) => {
if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
self.universal_regions.defining_ty
{
if tcx.is_static(def_id) {
ConstraintCategory::UseAsStatic
} else {
ConstraintCategory::UseAsConst
}
} else {
ConstraintCategory::Return(ReturnConstraint::Normal)
}
}
Some(l) if !self.body.local_decls[l].is_user_variable() => {
ConstraintCategory::Boring
}
// The return type of a call is interesting for diagnostics.
_ => ConstraintCategory::Assignment,
};
let locations = term_location.to_locations();
if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
span_mirbug!(
self,
term,
"call dest mismatch ({:?} <- {:?}): {:?}",
dest_ty,
sig.output(),
terr
);
}
// When `unsized_fn_params` is not enabled,
// this check is done at `check_local`.
if self.unsized_feature_enabled() {
let span = term.source_info.span;
self.ensure_place_sized(dest_ty, span);
}
}
}

View file

@ -5,10 +5,13 @@ use std::assert_matches::assert_matches;
use std::iter;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, GenericParamKind, attr, join_path_idents};
use rustc_ast::{self as ast, GenericParamKind, HasNodeId, attr, join_path_idents};
use rustc_ast_pretty::pprust;
use rustc_attr_parsing::AttributeParser;
use rustc_errors::{Applicability, Diag, Level};
use rustc_expand::base::*;
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Ident, Span, Symbol, sym};
use thin_vec::{ThinVec, thin_vec};
use tracing::debug;
@ -473,39 +476,19 @@ fn should_ignore_message(i: &ast::Item) -> Option<Symbol> {
}
fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
match attr::find_by_name(&i.attrs, sym::should_panic) {
Some(attr) => {
match attr.meta_item_list() {
// Handle #[should_panic(expected = "foo")]
Some(list) => {
let msg = list
.iter()
.find(|mi| mi.has_name(sym::expected))
.and_then(|mi| mi.meta_item())
.and_then(|mi| mi.value_str());
if list.len() != 1 || msg.is_none() {
cx.dcx()
.struct_span_warn(
attr.span,
"argument must be of the form: \
`expected = \"error message\"`",
)
.with_note(
"errors in this attribute were erroneously \
allowed and will become a hard error in a \
future release",
)
.emit();
ShouldPanic::Yes(None)
} else {
ShouldPanic::Yes(msg)
}
}
// Handle #[should_panic] and #[should_panic = "expected"]
None => ShouldPanic::Yes(attr.value_str()),
}
}
None => ShouldPanic::No,
if let Some(Attribute::Parsed(AttributeKind::ShouldPanic { reason, .. })) =
AttributeParser::parse_limited(
cx.sess,
&i.attrs,
sym::should_panic,
i.span,
i.node_id(),
None,
)
{
ShouldPanic::Yes(reason)
} else {
ShouldPanic::No
}
}

View file

@ -310,7 +310,10 @@ fn data_id_for_static(
// `extern_with_linkage_foo` will instead be initialized to
// zero.
let ref_name = format!("_rust_extern_with_linkage_{}", symbol_name);
let ref_name = format!(
"_rust_extern_with_linkage_{:016x}_{symbol_name}",
tcx.stable_crate_id(LOCAL_CRATE)
);
let ref_data_id = module.declare_data(&ref_name, Linkage::Local, false, false).unwrap();
let mut data = DataDescription::new();
data.set_align(align);

View file

@ -969,7 +969,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let layout = amount.layout();
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
ty::Uint(_) | ty::Int(_) => {}
_ => {
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return Ok(());
@ -982,7 +982,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let old =
fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount);
let old = CValue::by_val(old, layout);
let old = CValue::by_val(old, ret.layout());
ret.write_cvalue(fx, old);
}
sym::atomic_xsub => {
@ -991,7 +991,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let layout = amount.layout();
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
ty::Uint(_) | ty::Int(_) => {}
_ => {
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return Ok(());
@ -1004,7 +1004,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let old =
fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount);
let old = CValue::by_val(old, layout);
let old = CValue::by_val(old, ret.layout());
ret.write_cvalue(fx, old);
}
sym::atomic_and => {
@ -1013,7 +1013,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let layout = src.layout();
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
ty::Uint(_) | ty::Int(_) => {}
_ => {
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return Ok(());
@ -1025,7 +1025,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::And, ptr, src);
let old = CValue::by_val(old, layout);
let old = CValue::by_val(old, ret.layout());
ret.write_cvalue(fx, old);
}
sym::atomic_or => {
@ -1034,7 +1034,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let layout = src.layout();
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
ty::Uint(_) | ty::Int(_) => {}
_ => {
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return Ok(());
@ -1046,7 +1046,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Or, ptr, src);
let old = CValue::by_val(old, layout);
let old = CValue::by_val(old, ret.layout());
ret.write_cvalue(fx, old);
}
sym::atomic_xor => {
@ -1055,7 +1055,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let layout = src.layout();
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
ty::Uint(_) | ty::Int(_) => {}
_ => {
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return Ok(());
@ -1067,7 +1067,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Xor, ptr, src);
let old = CValue::by_val(old, layout);
let old = CValue::by_val(old, ret.layout());
ret.write_cvalue(fx, old);
}
sym::atomic_nand => {
@ -1076,7 +1076,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let layout = src.layout();
match layout.ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
ty::Uint(_) | ty::Int(_) => {}
_ => {
report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty);
return Ok(());
@ -1088,7 +1088,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Nand, ptr, src);
let old = CValue::by_val(old, layout);
let old = CValue::by_val(old, ret.layout());
ret.write_cvalue(fx, old);
}
sym::atomic_max => {

View file

@ -1671,6 +1671,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
dst: RValue<'gcc>,
src: RValue<'gcc>,
order: AtomicOrdering,
ret_ptr: bool,
) -> RValue<'gcc> {
let size = get_maybe_pointer_size(src);
let name = match op {
@ -1698,6 +1699,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
let atomic_function = self.context.get_builtin_function(name);
let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
// FIXME: If `ret_ptr` is true and `src` is an integer, we should really tell GCC
// that this is a pointer operation that needs to preserve provenance -- but like LLVM,
// GCC does not currently seems to support that.
let void_ptr_type = self.context.new_type::<*mut ()>();
let volatile_void_ptr_type = void_ptr_type.make_volatile();
let dst = self.context.new_cast(self.location, dst, volatile_void_ptr_type);
@ -1705,7 +1709,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
let new_src_type = atomic_function.get_param(1).to_rvalue().get_type();
let src = self.context.new_bitcast(self.location, src, new_src_type);
let res = self.context.new_call(self.location, atomic_function, &[dst, src, order]);
self.context.new_cast(self.location, res, src.get_type())
let res_type = if ret_ptr { void_ptr_type } else { src.get_type() };
self.context.new_cast(self.location, res, res_type)
}
fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope) {

View file

@ -6,6 +6,7 @@ use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint,
@ -384,8 +385,8 @@ fn check_and_apply_linkage<'gcc, 'tcx>(
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
// zero.
let mut real_name = "_rust_extern_with_linkage_".to_string();
real_name.push_str(sym);
let real_name =
format!("_rust_extern_with_linkage_{:016x}_{sym}", cx.tcx.stable_crate_id(LOCAL_CRATE));
let global2 = cx.define_global(&real_name, gcc_type, is_tls, attrs.link_section);
// TODO(antoyo): set linkage.
let value = cx.const_ptrcast(global1.get_address(None), gcc_type);

View file

@ -1327,15 +1327,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
&mut self,
op: rustc_codegen_ssa::common::AtomicRmwBinOp,
dst: &'ll Value,
mut src: &'ll Value,
src: &'ll Value,
order: rustc_middle::ty::AtomicOrdering,
ret_ptr: bool,
) -> &'ll Value {
// The only RMW operation that LLVM supports on pointers is compare-exchange.
let requires_cast_to_int = self.val_ty(src) == self.type_ptr()
&& op != rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicXchg;
if requires_cast_to_int {
src = self.ptrtoint(src, self.type_isize());
}
// FIXME: If `ret_ptr` is true and `src` is not a pointer, we *should* tell LLVM that the
// LHS is a pointer and the operation should be provenance-preserving, but LLVM does not
// currently support that (https://github.com/llvm/llvm-project/issues/120837).
let mut res = unsafe {
llvm::LLVMBuildAtomicRMW(
self.llbuilder,
@ -1346,7 +1344,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
llvm::False, // SingleThreaded
)
};
if requires_cast_to_int {
if ret_ptr && self.val_ty(res) != self.type_ptr() {
res = self.inttoptr(res, self.type_ptr());
}
res
@ -1886,48 +1884,4 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
) {
self.call_intrinsic("llvm.instrprof.increment", &[], &[fn_name, hash, num_counters, index]);
}
/// Emits a call to `llvm.instrprof.mcdc.parameters`.
///
/// This doesn't produce any code directly, but is used as input by
/// the LLVM pass that handles coverage instrumentation.
///
/// (See clang's [`CodeGenPGO::emitMCDCParameters`] for comparison.)
///
/// [`CodeGenPGO::emitMCDCParameters`]:
/// https://github.com/rust-lang/llvm-project/blob/5399a24/clang/lib/CodeGen/CodeGenPGO.cpp#L1124
#[instrument(level = "debug", skip(self))]
pub(crate) fn mcdc_parameters(
&mut self,
fn_name: &'ll Value,
hash: &'ll Value,
bitmap_bits: &'ll Value,
) {
self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[], &[fn_name, hash, bitmap_bits]);
}
#[instrument(level = "debug", skip(self))]
pub(crate) fn mcdc_tvbitmap_update(
&mut self,
fn_name: &'ll Value,
hash: &'ll Value,
bitmap_index: &'ll Value,
mcdc_temp: &'ll Value,
) {
let args = &[fn_name, hash, bitmap_index, mcdc_temp];
self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", &[], args);
}
#[instrument(level = "debug", skip(self))]
pub(crate) fn mcdc_condbitmap_reset(&mut self, mcdc_temp: &'ll Value) {
self.store(self.const_i32(0), mcdc_temp, self.tcx.data_layout.i32_align.abi);
}
#[instrument(level = "debug", skip(self))]
pub(crate) fn mcdc_condbitmap_update(&mut self, cond_index: &'ll Value, mcdc_temp: &'ll Value) {
let align = self.tcx.data_layout.i32_align.abi;
let current_tv_index = self.load(self.cx.type_i32(), mcdc_temp, align);
let new_tv_index = self.add(current_tv_index, cond_index);
self.store(new_tv_index, mcdc_temp, align);
}
}

View file

@ -5,7 +5,7 @@ use rustc_codegen_ssa::common;
use rustc_codegen_ssa::traits::*;
use rustc_hir::LangItem;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
Allocation, ConstAllocation, ErrorHandled, InitChunk, Pointer, Scalar as InterpScalar,
@ -191,8 +191,8 @@ fn check_and_apply_linkage<'ll, 'tcx>(
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
// zero.
let mut real_name = "_rust_extern_with_linkage_".to_string();
real_name.push_str(sym);
let real_name =
format!("_rust_extern_with_linkage_{:016x}_{sym}", cx.tcx.stable_crate_id(LOCAL_CRATE));
let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| {
cx.sess().dcx().emit_fatal(SymbolAlreadyDefined {
span: cx.tcx.def_span(def_id),

View file

@ -73,48 +73,6 @@ pub(crate) struct CounterExpression {
pub(crate) rhs: Counter,
}
pub(crate) mod mcdc {
use rustc_middle::mir::coverage::{ConditionId, ConditionInfo, DecisionInfo};
/// Must match the layout of `LLVMRustMCDCDecisionParameters`.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct DecisionParameters {
bitmap_idx: u32,
num_conditions: u16,
}
type LLVMConditionId = i16;
/// Must match the layout of `LLVMRustMCDCBranchParameters`.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct BranchParameters {
condition_id: LLVMConditionId,
condition_ids: [LLVMConditionId; 2],
}
impl From<ConditionInfo> for BranchParameters {
fn from(value: ConditionInfo) -> Self {
let to_llvm_cond_id = |cond_id: Option<ConditionId>| {
cond_id.and_then(|id| LLVMConditionId::try_from(id.as_usize()).ok()).unwrap_or(-1)
};
let ConditionInfo { condition_id, true_next_id, false_next_id } = value;
Self {
condition_id: to_llvm_cond_id(Some(condition_id)),
condition_ids: [to_llvm_cond_id(false_next_id), to_llvm_cond_id(true_next_id)],
}
}
}
impl From<DecisionInfo> for DecisionParameters {
fn from(info: DecisionInfo) -> Self {
let DecisionInfo { bitmap_idx, num_conditions } = info;
Self { bitmap_idx, num_conditions }
}
}
}
/// A span of source code coordinates to be embedded in coverage metadata.
///
/// Must match the layout of `LLVMRustCoverageSpan`.
@ -148,26 +106,14 @@ pub(crate) struct Regions {
pub(crate) code_regions: Vec<CodeRegion>,
pub(crate) expansion_regions: Vec<ExpansionRegion>,
pub(crate) branch_regions: Vec<BranchRegion>,
pub(crate) mcdc_branch_regions: Vec<MCDCBranchRegion>,
pub(crate) mcdc_decision_regions: Vec<MCDCDecisionRegion>,
}
impl Regions {
/// Returns true if none of this structure's tables contain any regions.
pub(crate) fn has_no_regions(&self) -> bool {
let Self {
code_regions,
expansion_regions,
branch_regions,
mcdc_branch_regions,
mcdc_decision_regions,
} = self;
let Self { code_regions, expansion_regions, branch_regions } = self;
code_regions.is_empty()
&& expansion_regions.is_empty()
&& branch_regions.is_empty()
&& mcdc_branch_regions.is_empty()
&& mcdc_decision_regions.is_empty()
code_regions.is_empty() && expansion_regions.is_empty() && branch_regions.is_empty()
}
}
@ -195,21 +141,3 @@ pub(crate) struct BranchRegion {
pub(crate) true_counter: Counter,
pub(crate) false_counter: Counter,
}
/// Must match the layout of `LLVMRustCoverageMCDCBranchRegion`.
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct MCDCBranchRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) true_counter: Counter,
pub(crate) false_counter: Counter,
pub(crate) mcdc_branch_params: mcdc::BranchParameters,
}
/// Must match the layout of `LLVMRustCoverageMCDCDecisionRegion`.
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct MCDCDecisionRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) mcdc_decision_params: mcdc::DecisionParameters,
}

View file

@ -63,13 +63,7 @@ pub(crate) fn write_function_mappings_to_buffer(
expressions: &[ffi::CounterExpression],
regions: &ffi::Regions,
) -> Vec<u8> {
let ffi::Regions {
code_regions,
expansion_regions,
branch_regions,
mcdc_branch_regions,
mcdc_decision_regions,
} = regions;
let ffi::Regions { code_regions, expansion_regions, branch_regions } = regions;
// SAFETY:
// - All types are FFI-compatible and have matching representations in Rust/C++.
@ -87,10 +81,6 @@ pub(crate) fn write_function_mappings_to_buffer(
expansion_regions.len(),
branch_regions.as_ptr(),
branch_regions.len(),
mcdc_branch_regions.as_ptr(),
mcdc_branch_regions.len(),
mcdc_decision_regions.as_ptr(),
mcdc_decision_regions.len(),
buffer,
)
})

View file

@ -140,8 +140,6 @@ fn fill_region_tables<'tcx>(
code_regions,
expansion_regions: _, // FIXME(Zalathar): Fill out support for expansion regions
branch_regions,
mcdc_branch_regions,
mcdc_decision_regions,
} = &mut covfun.regions;
// For each counter/region pair in this function+file, convert it to a
@ -161,20 +159,6 @@ fn fill_region_tables<'tcx>(
false_counter: counter_for_bcb(false_bcb),
});
}
MappingKind::MCDCBranch { true_bcb, false_bcb, mcdc_params } => {
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
cov_span,
true_counter: counter_for_bcb(true_bcb),
false_counter: counter_for_bcb(false_bcb),
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
});
}
MappingKind::MCDCDecision(mcdc_decision_params) => {
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
cov_span,
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
});
}
}
}
}

View file

@ -1,11 +1,10 @@
use std::cell::{OnceCell, RefCell};
use std::ffi::{CStr, CString};
use rustc_abi::Size;
use rustc_codegen_ssa::traits::{
BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::fx::FxIndexMap;
use rustc_middle::mir::coverage::CoverageKind;
use rustc_middle::ty::Instance;
use tracing::{debug, instrument};
@ -28,34 +27,13 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> {
/// symbol name, and `llvm-cov` will exit fatally if it can't resolve that
/// hash back to an entry in the binary's `__llvm_prf_names` linker section.
pub(crate) pgo_func_name_var_map: RefCell<FxIndexMap<Instance<'tcx>, &'ll llvm::Value>>,
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
covfun_section_name: OnceCell<CString>,
}
impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
pub(crate) fn new() -> Self {
Self {
pgo_func_name_var_map: Default::default(),
mcdc_condition_bitmap_map: Default::default(),
covfun_section_name: Default::default(),
}
}
/// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is
/// called condition bitmap. In order to handle nested decisions, several condition bitmaps can
/// be allocated for a function body. These values are named `mcdc.addr.{i}` and are a 32-bit
/// integers. They respectively hold the condition bitmaps for decisions with a depth of `i`.
fn try_get_mcdc_condition_bitmap(
&self,
instance: &Instance<'tcx>,
decision_depth: u16,
) -> Option<&'ll llvm::Value> {
self.mcdc_condition_bitmap_map
.borrow()
.get(instance)
.and_then(|bitmap_map| bitmap_map.get(decision_depth as usize))
.copied() // Dereference Option<&&Value> to Option<&Value>
Self { pgo_func_name_var_map: Default::default(), covfun_section_name: Default::default() }
}
/// Returns the list of instances considered "used" in this CGU, as
@ -105,38 +83,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
}
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
fn init_coverage(&mut self, instance: Instance<'tcx>) {
let Some(function_coverage_info) =
self.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
else {
return;
};
// If there are no MC/DC bitmaps to set up, return immediately.
if function_coverage_info.mcdc_bitmap_bits == 0 {
return;
}
let fn_name = self.ensure_pgo_func_name_var(instance);
let hash = self.const_u64(function_coverage_info.function_source_hash);
let bitmap_bits = self.const_u32(function_coverage_info.mcdc_bitmap_bits as u32);
self.mcdc_parameters(fn_name, hash, bitmap_bits);
// Create pointers named `mcdc.addr.{i}` to stack-allocated condition bitmaps.
let mut cond_bitmaps = vec![];
for i in 0..function_coverage_info.mcdc_num_condition_bitmaps {
// MC/DC intrinsics will perform loads/stores that use the ABI default
// alignment for i32, so our variable declaration should match.
let align = self.tcx.data_layout.i32_align.abi;
let cond_bitmap = self.alloca(Size::from_bytes(4), align);
llvm::set_value_name(cond_bitmap, format!("mcdc.addr.{i}").as_bytes());
self.store(self.const_i32(0), cond_bitmap, align);
cond_bitmaps.push(cond_bitmap);
}
self.coverage_cx().mcdc_condition_bitmap_map.borrow_mut().insert(instance, cond_bitmaps);
}
#[instrument(level = "debug", skip(self))]
fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
// Our caller should have already taken care of inlining subtleties,
@ -153,7 +99,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
// When that happens, we currently just discard those statements, so
// the corresponding code will be undercounted.
// FIXME(Zalathar): Find a better solution for mixed-coverage builds.
let Some(coverage_cx) = &bx.cx.coverage_cx else { return };
let Some(_coverage_cx) = &bx.cx.coverage_cx else { return };
let Some(function_coverage_info) =
bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
@ -185,30 +131,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
}
// If a BCB doesn't have an associated physical counter, there's nothing to codegen.
CoverageKind::VirtualCounter { .. } => {}
CoverageKind::CondBitmapUpdate { index, decision_depth } => {
let cond_bitmap = coverage_cx
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
.expect("mcdc cond bitmap should have been allocated for updating");
let cond_index = bx.const_i32(index as i32);
bx.mcdc_condbitmap_update(cond_index, cond_bitmap);
}
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
let cond_bitmap =
coverage_cx.try_get_mcdc_condition_bitmap(&instance, decision_depth).expect(
"mcdc cond bitmap should have been allocated for merging \
into the global bitmap",
);
assert!(
bitmap_idx as usize <= function_coverage_info.mcdc_bitmap_bits,
"bitmap index of the decision out of range"
);
let fn_name = bx.ensure_pgo_func_name_var(instance);
let hash = bx.const_u64(function_coverage_info.function_source_hash);
let bitmap_index = bx.const_u32(bitmap_idx);
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap);
bx.mcdc_condbitmap_reset(cond_bitmap);
}
}
}
}

View file

@ -2056,10 +2056,6 @@ unsafe extern "C" {
NumExpansionRegions: size_t,
BranchRegions: *const crate::coverageinfo::ffi::BranchRegion,
NumBranchRegions: size_t,
MCDCBranchRegions: *const crate::coverageinfo::ffi::MCDCBranchRegion,
NumMCDCBranchRegions: size_t,
MCDCDecisionRegions: *const crate::coverageinfo::ffi::MCDCDecisionRegion,
NumMCDCDecisionRegions: size_t,
BufferOut: &RustString,
);

View file

@ -262,6 +262,15 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
// Filter out features that are not supported by the current LLVM version
("aarch64", "fpmr") => None, // only existed in 18
("arm", "fp16") => Some(LLVMFeature::new("fullfp16")),
// NVPTX targets added in LLVM 20
("nvptx64", "sm_100") if get_version().0 < 20 => None,
("nvptx64", "sm_100a") if get_version().0 < 20 => None,
("nvptx64", "sm_101") if get_version().0 < 20 => None,
("nvptx64", "sm_101a") if get_version().0 < 20 => None,
("nvptx64", "sm_120") if get_version().0 < 20 => None,
("nvptx64", "sm_120a") if get_version().0 < 20 => None,
("nvptx64", "ptx86") if get_version().0 < 20 => None,
("nvptx64", "ptx87") if get_version().0 < 20 => None,
// Filter out features that are not supported by the current LLVM version
("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq")
if get_version().0 < 20 =>
@ -324,15 +333,12 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
///
/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled outside codegen.
pub(crate) fn target_config(sess: &Session) -> TargetConfig {
// Add base features for the target.
// We do *not* add the -Ctarget-features there, and instead duplicate the logic for that below.
// The reason is that if LLVM considers a feature implied but we do not, we don't want that to
// show up in `cfg`. That way, `cfg` is entirely under our control -- except for the handling of
// the target CPU, that is still expanded to target features (with all their implied features)
// by LLVM.
let target_machine = create_informational_target_machine(sess, true);
let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| {
// This closure determines whether the target CPU has the feature according to LLVM. We do
// *not* consider the `-Ctarget-feature`s here, as that will be handled later in
// `cfg_target_feature`.
if let Some(feat) = to_llvm_features(sess, feature) {
// All the LLVM features this expands to must be enabled.
for llvm_feature in feat {
@ -371,24 +377,25 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
let target_abi = sess.target.options.abi.as_ref();
let target_pointer_width = sess.target.pointer_width;
let version = get_version();
let lt_20_1_1 = version < (20, 1, 1);
let lt_21_0_0 = version < (21, 0, 0);
cfg.has_reliable_f16 = match (target_arch, target_os) {
// Selection failure <https://github.com/llvm/llvm-project/issues/50374>
("s390x", _) => false,
// LLVM crash without neon <https://github.com/llvm/llvm-project/issues/129394> (now fixed)
// LLVM crash without neon <https://github.com/llvm/llvm-project/issues/129394> (fixed in llvm20)
("aarch64", _)
if !cfg.target_features.iter().any(|f| f.as_str() == "neon")
&& version < (20, 1, 1) =>
if !cfg.target_features.iter().any(|f| f.as_str() == "neon") && lt_20_1_1 =>
{
false
}
// Unsupported <https://github.com/llvm/llvm-project/issues/94434>
("arm64ec", _) => false,
// Selection failure <https://github.com/llvm/llvm-project/issues/50374> (fixed in llvm21)
("s390x", _) if lt_21_0_0 => false,
// MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054>
("x86_64", "windows") if target_env == "gnu" && target_abi != "llvm" => false,
// Infinite recursion <https://github.com/llvm/llvm-project/issues/97981>
("csky", _) => false,
("hexagon", _) => false,
("hexagon", _) if lt_21_0_0 => false, // (fixed in llvm21)
("powerpc" | "powerpc64", _) => false,
("sparc" | "sparc64", _) => false,
("wasm32" | "wasm64", _) => false,
@ -401,9 +408,10 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
cfg.has_reliable_f128 = match (target_arch, target_os) {
// Unsupported <https://github.com/llvm/llvm-project/issues/94434>
("arm64ec", _) => false,
// Selection bug <https://github.com/llvm/llvm-project/issues/96432>
("mips64" | "mips64r6", _) => false,
// Selection bug <https://github.com/llvm/llvm-project/issues/95471>
// Selection bug <https://github.com/llvm/llvm-project/issues/96432> (fixed in llvm20)
("mips64" | "mips64r6", _) if lt_20_1_1 => false,
// Selection bug <https://github.com/llvm/llvm-project/issues/95471>. This issue is closed
// but basic math still does not work.
("nvptx64", _) => false,
// Unsupported https://github.com/llvm/llvm-project/issues/121122
("amdgpu", _) => false,
@ -413,8 +421,8 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
// ABI unsupported <https://github.com/llvm/llvm-project/issues/41838>
("sparc", _) => false,
// Stack alignment bug <https://github.com/llvm/llvm-project/issues/77401>. NB: tests may
// not fail if our compiler-builtins is linked.
("x86", _) => false,
// not fail if our compiler-builtins is linked. (fixed in llvm21)
("x86", _) if lt_21_0_0 => false,
// MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054>
("x86_64", "windows") if target_env == "gnu" && target_abi != "llvm" => false,
// There are no known problems on other platforms, so the only requirement is that symbols

View file

@ -101,6 +101,8 @@ codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization
codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}`
codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer or pointer type, found `{$ty}`
codegen_ssa_invalid_monomorphization_cannot_return = invalid monomorphization of `{$name}` intrinsic: cannot return `{$ret_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
codegen_ssa_invalid_monomorphization_cast_wide_pointer = invalid monomorphization of `{$name}` intrinsic: cannot cast wide pointer `{$ty}`

View file

@ -69,6 +69,15 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTr
set_reuse(&mut ams.cgu_reuse_tracker);
if tcx.sess.opts.unstable_opts.print_mono_items
&& let Some(data) = &ams.cgu_reuse_tracker.data
{
data.actual_reuse.items().all(|(cgu, reuse)| {
println!("CGU_REUSE {cgu} {reuse}");
true
});
}
ams.cgu_reuse_tracker.check_expected_reuse(tcx.sess);
});
}

View file

@ -17,7 +17,7 @@ mod tests;
/// The canonical name of the desired SDK for a given target.
pub(super) fn sdk_name(target: &Target) -> &'static str {
match (&*target.os, &*target.abi) {
match (&*target.os, &*target.env) {
("macos", "") => "MacOSX",
("ios", "") => "iPhoneOS",
("ios", "sim") => "iPhoneSimulator",
@ -34,7 +34,7 @@ pub(super) fn sdk_name(target: &Target) -> &'static str {
}
pub(super) fn macho_platform(target: &Target) -> u32 {
match (&*target.os, &*target.abi) {
match (&*target.os, &*target.env) {
("macos", _) => object::macho::PLATFORM_MACOS,
("ios", "macabi") => object::macho::PLATFORM_MACCATALYST,
("ios", "sim") => object::macho::PLATFORM_IOSSIMULATOR,

View file

@ -3026,7 +3026,7 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool
/// We need to communicate five things to the linker on Apple/Darwin targets:
/// - The architecture.
/// - The operating system (and that it's an Apple platform).
/// - The environment / ABI.
/// - The environment.
/// - The deployment target.
/// - The SDK version.
fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
@ -3040,7 +3040,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
// `sess.target.arch` (`target_arch`) is not detailed enough.
let llvm_arch = sess.target.llvm_target.split_once('-').expect("LLVM target must have arch").0;
let target_os = &*sess.target.os;
let target_abi = &*sess.target.abi;
let target_env = &*sess.target.env;
// The architecture name to forward to the linker.
//
@ -3091,14 +3091,14 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
// > - visionos-simulator
// > - xros-simulator
// > - driverkit
let platform_name = match (target_os, target_abi) {
let platform_name = match (target_os, target_env) {
(os, "") => os,
("ios", "macabi") => "mac-catalyst",
("ios", "sim") => "ios-simulator",
("tvos", "sim") => "tvos-simulator",
("watchos", "sim") => "watchos-simulator",
("visionos", "sim") => "visionos-simulator",
_ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"),
_ => bug!("invalid OS/env combination for Apple target: {target_os}, {target_env}"),
};
let min_version = sess.apple_deployment_target().fmt_full().to_string();

View file

@ -1805,11 +1805,18 @@ pub(crate) fn exported_symbols(
.collect();
}
if let CrateType::ProcMacro = crate_type {
let mut symbols = if let CrateType::ProcMacro = crate_type {
exported_symbols_for_proc_macro_crate(tcx)
} else {
exported_symbols_for_non_proc_macro(tcx, crate_type)
};
if crate_type == CrateType::Dylib || crate_type == CrateType::ProcMacro {
let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);
symbols.push((metadata_symbol_name, SymbolExportKind::Data));
}
symbols
}
fn exported_symbols_for_non_proc_macro(
@ -1842,12 +1849,8 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, Symbol
let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE);
let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);
vec![
(proc_macro_decls_name, SymbolExportKind::Data),
(metadata_symbol_name, SymbolExportKind::Data),
]
vec![(proc_macro_decls_name, SymbolExportKind::Data)]
}
pub(crate) fn linked_symbols(

View file

@ -8,7 +8,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId};
use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::exported_symbols::{
ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, metadata_symbol_name,
ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,
};
use rustc_middle::query::LocalCrate;
use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, Instance, SymbolName, Ty, TyCtxt};
@ -289,23 +289,6 @@ fn exported_non_generic_symbols_provider_local<'tcx>(
}));
}
if tcx.crate_types().contains(&CrateType::Dylib)
|| tcx.crate_types().contains(&CrateType::ProcMacro)
{
let symbol_name = metadata_symbol_name(tcx);
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
symbols.push((
exported_symbol,
SymbolExportInfo {
level: SymbolExportLevel::C,
kind: SymbolExportKind::Data,
used: true,
rustc_std_internal_symbol: false,
},
));
}
// Sort so we get a stable incr. comp. hash.
symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx));

View file

@ -764,6 +764,14 @@ pub enum InvalidMonomorphization<'tcx> {
ty: Ty<'tcx>,
},
#[diag(codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type, code = E0511)]
BasicIntegerOrPtrType {
#[primary_span]
span: Span,
name: Symbol,
ty: Ty<'tcx>,
},
#[diag(codegen_ssa_invalid_monomorphization_basic_float_type, code = E0511)]
BasicFloatType {
#[primary_span]

View file

@ -92,6 +92,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let invalid_monomorphization_int_type = |ty| {
bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
};
let invalid_monomorphization_int_or_ptr_type = |ty| {
bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerOrPtrType {
span,
name,
ty,
});
};
let parse_atomic_ordering = |ord: ty::Value<'tcx>| {
let discr = ord.valtree.unwrap_branch()[0].unwrap_leaf();
@ -351,7 +358,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
sym::atomic_load => {
let ty = fn_args.type_at(0);
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
invalid_monomorphization_int_type(ty);
invalid_monomorphization_int_or_ptr_type(ty);
return Ok(());
}
let ordering = fn_args.const_at(1).to_value();
@ -367,7 +374,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
sym::atomic_store => {
let ty = fn_args.type_at(0);
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
invalid_monomorphization_int_type(ty);
invalid_monomorphization_int_or_ptr_type(ty);
return Ok(());
}
let ordering = fn_args.const_at(1).to_value();
@ -377,10 +384,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.atomic_store(val, ptr, parse_atomic_ordering(ordering), size);
return Ok(());
}
// These are all AtomicRMW ops
sym::atomic_cxchg | sym::atomic_cxchgweak => {
let ty = fn_args.type_at(0);
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
invalid_monomorphization_int_type(ty);
invalid_monomorphization_int_or_ptr_type(ty);
return Ok(());
}
let succ_ordering = fn_args.const_at(1).to_value();
@ -407,7 +415,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return Ok(());
}
// These are all AtomicRMW ops
sym::atomic_max | sym::atomic_min => {
let atom_op = if name == sym::atomic_max {
AtomicRmwBinOp::AtomicMax
@ -420,7 +427,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let ordering = fn_args.const_at(1).to_value();
let ptr = args[0].immediate();
let val = args[1].immediate();
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
bx.atomic_rmw(
atom_op,
ptr,
val,
parse_atomic_ordering(ordering),
/* ret_ptr */ false,
)
} else {
invalid_monomorphization_int_type(ty);
return Ok(());
@ -438,21 +451,44 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let ordering = fn_args.const_at(1).to_value();
let ptr = args[0].immediate();
let val = args[1].immediate();
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
bx.atomic_rmw(
atom_op,
ptr,
val,
parse_atomic_ordering(ordering),
/* ret_ptr */ false,
)
} else {
invalid_monomorphization_int_type(ty);
return Ok(());
}
}
sym::atomic_xchg
| sym::atomic_xadd
sym::atomic_xchg => {
let ty = fn_args.type_at(0);
let ordering = fn_args.const_at(1).to_value();
if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
let ptr = args[0].immediate();
let val = args[1].immediate();
let atomic_op = AtomicRmwBinOp::AtomicXchg;
bx.atomic_rmw(
atomic_op,
ptr,
val,
parse_atomic_ordering(ordering),
/* ret_ptr */ ty.is_raw_ptr(),
)
} else {
invalid_monomorphization_int_or_ptr_type(ty);
return Ok(());
}
}
sym::atomic_xadd
| sym::atomic_xsub
| sym::atomic_and
| sym::atomic_nand
| sym::atomic_or
| sym::atomic_xor => {
let atom_op = match name {
sym::atomic_xchg => AtomicRmwBinOp::AtomicXchg,
sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
@ -462,14 +498,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
_ => unreachable!(),
};
let ty = fn_args.type_at(0);
if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
let ordering = fn_args.const_at(1).to_value();
let ptr = args[0].immediate();
let val = args[1].immediate();
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
// The type of the in-memory data.
let ty_mem = fn_args.type_at(0);
// The type of the 2nd operand, given by-value.
let ty_op = fn_args.type_at(1);
let ordering = fn_args.const_at(2).to_value();
// We require either both arguments to have the same integer type, or the first to
// be a pointer and the second to be `usize`.
if (int_type_width_signed(ty_mem, bx.tcx()).is_some() && ty_op == ty_mem)
|| (ty_mem.is_raw_ptr() && ty_op == bx.tcx().types.usize)
{
let ptr = args[0].immediate(); // of type "pointer to `ty_mem`"
let val = args[1].immediate(); // of type `ty_op`
bx.atomic_rmw(
atom_op,
ptr,
val,
parse_atomic_ordering(ordering),
/* ret_ptr */ ty_mem.is_raw_ptr(),
)
} else {
invalid_monomorphization_int_type(ty);
invalid_monomorphization_int_or_ptr_type(ty_mem);
return Ok(());
}
}

View file

@ -296,10 +296,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// Apply debuginfo to the newly allocated locals.
fx.debug_introduce_locals(&mut start_bx, consts_debug_info.unwrap_or_default());
// If the backend supports coverage, and coverage is enabled for this function,
// do any necessary start-of-function codegen (e.g. locals for MC/DC bitmaps).
start_bx.init_coverage(instance);
// The builders will be created separately for each basic block at `codegen_block`.
// So drop the builder of `start_llbb` to avoid having two at the same time.
drop(start_bx);

View file

@ -498,6 +498,35 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64);
(is_niche, tagged_discr, 0)
} else {
// Thanks to parameter attributes and load metadata, LLVM already knows
// the general valid range of the tag. It's possible, though, for there
// to be an impossible value *in the middle*, which those ranges don't
// communicate, so it's worth an `assume` to let the optimizer know.
// Most importantly, this means when optimizing a variant test like
// `SELECT(is_niche, complex, CONST) == CONST` it's ok to simplify that
// to `!is_niche` because the `complex` part can't possibly match.
//
// This was previously asserted on `tagged_discr` below, where the
// impossible value is more obvious, but that caused an intermediate
// value to become multi-use and thus not optimize, so instead this
// assumes on the original input which is always multi-use. See
// <https://github.com/llvm/llvm-project/issues/134024#issuecomment-3131782555>
//
// FIXME: If we ever get range assume operand bundles in LLVM (so we
// don't need the `icmp`s in the instruction stream any more), it
// might be worth moving this back to being on the switch argument
// where it's more obviously applicable.
if niche_variants.contains(&untagged_variant)
&& bx.cx().sess().opts.optimize != OptLevel::No
{
let impossible = niche_start
.wrapping_add(u128::from(untagged_variant.as_u32()))
.wrapping_sub(u128::from(niche_variants.start().as_u32()));
let impossible = bx.cx().const_uint_big(tag_llty, impossible);
let ne = bx.icmp(IntPredicate::IntNE, tag, impossible);
bx.assume(ne);
}
// With multiple niched variants we'll have to actually compute
// the variant index from the stored tag.
//
@ -588,20 +617,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
let untagged_variant_const =
bx.cx().const_uint(cast_to, u64::from(untagged_variant.as_u32()));
// Thanks to parameter attributes and load metadata, LLVM already knows
// the general valid range of the tag. It's possible, though, for there
// to be an impossible value *in the middle*, which those ranges don't
// communicate, so it's worth an `assume` to let the optimizer know.
// Most importantly, this means when optimizing a variant test like
// `SELECT(is_niche, complex, CONST) == CONST` it's ok to simplify that
// to `!is_niche` because the `complex` part can't possibly match.
if niche_variants.contains(&untagged_variant)
&& bx.cx().sess().opts.optimize != OptLevel::No
{
let ne = bx.icmp(IntPredicate::IntNE, tagged_discr, untagged_variant_const);
bx.assume(ne);
}
let discr = bx.select(is_niche, tagged_discr, untagged_variant_const);
// In principle we could insert assumes on the possible range of `discr`, but

View file

@ -197,7 +197,10 @@ fn parse_rust_feature_flag<'a>(
/// 2nd component of the return value, respectively).
///
/// `target_base_has_feature` should check whether the given feature (a Rust feature name!) is
/// enabled in the "base" target machine, i.e., without applying `-Ctarget-feature`.
/// enabled in the "base" target machine, i.e., without applying `-Ctarget-feature`. Note that LLVM
/// may consider features to be implied that we do not and vice-versa. We want `cfg` to be entirely
/// consistent with Rust feature implications, and thus only consult LLVM to expand the target CPU
/// to target features.
///
/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere.
pub fn cfg_target_feature(
@ -211,7 +214,15 @@ pub fn cfg_target_feature(
.rust_target_features()
.iter()
.filter(|(feature, _, _)| target_base_has_feature(feature))
.map(|(feature, _, _)| Symbol::intern(feature))
.flat_map(|(base_feature, _, _)| {
// Expand the direct base feature into all transitively-implied features. Note that we
// cannot simply use the `implied` field of the tuple since that only contains
// directly-implied features.
//
// Iteration order is irrelevant because we're collecting into an `UnordSet`.
#[allow(rustc::potential_query_instability)]
sess.target.implied_target_features(base_feature).into_iter().map(|f| Symbol::intern(f))
})
.collect();
// Add enabled and remove disabled features.

View file

@ -548,12 +548,15 @@ pub trait BuilderMethods<'a, 'tcx>:
failure_order: AtomicOrdering,
weak: bool,
) -> (Self::Value, Self::Value);
/// `ret_ptr` indicates whether the return type (which is also the type `dst` points to)
/// is a pointer or the same type as `src`.
fn atomic_rmw(
&mut self,
op: AtomicRmwBinOp,
dst: Self::Value,
src: Self::Value,
order: AtomicOrdering,
ret_ptr: bool,
) -> Self::Value;
fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope);
fn set_invariant_load(&mut self, load: Self::Value);

View file

@ -2,11 +2,6 @@ use rustc_middle::mir::coverage::CoverageKind;
use rustc_middle::ty::Instance;
pub trait CoverageInfoBuilderMethods<'tcx> {
/// Performs any start-of-function codegen needed for coverage instrumentation.
///
/// Can be a no-op in backends that don't support coverage instrumentation.
fn init_coverage(&mut self, _instance: Instance<'tcx>) {}
/// Handle the MIR coverage info in a backend-specific way.
///
/// This can potentially be a no-op in backends that don't support

View file

@ -142,7 +142,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|err, self_ty, trait_id| {
// FIXME(const_trait_impl): Do we need any of this on the non-const codepath?
let trait_ref = TraitRef::from_method(tcx, trait_id, self.args);
let trait_ref = TraitRef::from_assoc(tcx, trait_id, self.args);
match self_ty.kind() {
Param(param_ty) => {

View file

@ -732,7 +732,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let tcx = *self.tcx;
let trait_def_id = tcx.trait_of_assoc(def_id).unwrap();
let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args);
let virtual_trait_ref = ty::TraitRef::from_assoc(tcx, trait_def_id, virtual_instance.args);
let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);

View file

@ -1,6 +1,7 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(bootstrap, feature(strict_overflow_ops))]
#![doc(rust_logo)]
#![feature(array_try_map)]
#![feature(assert_matches)]
@ -10,7 +11,6 @@
#![feature(never_type)]
#![feature(rustdoc_internals)]
#![feature(slice_ptr_get)]
#![feature(strict_overflow_ops)]
#![feature(trait_alias)]
#![feature(try_blocks)]
#![feature(unqualified_local_imports)]

View file

@ -18,7 +18,9 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
}
fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
unreachable!(); // because `<Self As PrettyPrinter>::should_print_region` returns false
// This is reachable (via `pretty_print_dyn_existential`) even though
// `<Self As PrettyPrinter>::should_print_region` returns false. See #144994.
Ok(())
}
fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {

View file

@ -1382,6 +1382,11 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
&mut self.long_ty_path
}
pub fn with_long_ty_path(mut self, long_ty_path: Option<PathBuf>) -> Self {
self.long_ty_path = long_ty_path;
self
}
/// Most `emit_producing_guarantee` functions use this as a starting point.
fn emit_producing_nothing(mut self) {
let diag = self.take_diag();

View file

@ -409,7 +409,7 @@ pub trait Emitter {
if !redundant_span || always_backtrace {
let msg: Cow<'static, _> = match trace.kind {
ExpnKind::Macro(MacroKind::Attr, _) => {
"this procedural macro expansion".into()
"this attribute macro expansion".into()
}
ExpnKind::Macro(MacroKind::Derive, _) => {
"this derive macro expansion".into()

View file

@ -70,6 +70,9 @@ expand_invalid_fragment_specifier =
invalid fragment specifier `{$fragment}`
.help = {$help}
expand_macro_args_bad_delim = macro attribute argument matchers require parentheses
expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)`
expand_macro_body_stability =
macros cannot have body stability attributes
.label = invalid body stability attribute

View file

@ -482,3 +482,21 @@ mod metavar_exprs {
pub key: MacroRulesNormalizedIdent,
}
}
#[derive(Diagnostic)]
#[diag(expand_macro_args_bad_delim)]
pub(crate) struct MacroArgsBadDelim {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: MacroArgsBadDelimSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(expand_macro_args_bad_delim_sugg, applicability = "machine-applicable")]
pub(crate) struct MacroArgsBadDelimSugg {
#[suggestion_part(code = "(")]
pub open: Span,
#[suggestion_part(code = ")")]
pub close: Span,
}

View file

@ -7,29 +7,40 @@ use rustc_macros::Subdiagnostic;
use rustc_parse::parser::{Parser, Recovery, token_descr};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::SourceMap;
use rustc_span::{ErrorGuaranteed, Ident, Span};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span};
use tracing::debug;
use super::macro_rules::{MacroRule, NoopTracker, parser_from_cx};
use crate::expand::{AstFragmentKind, parse_ast_fragment};
use crate::mbe::macro_parser::ParseResult::*;
use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
use crate::mbe::macro_rules::{Tracker, try_match_macro};
use crate::mbe::macro_rules::{Tracker, try_match_macro, try_match_macro_attr};
pub(super) fn failed_to_match_macro(
psess: &ParseSess,
sp: Span,
def_span: Span,
name: Ident,
arg: TokenStream,
attr_args: Option<&TokenStream>,
body: &TokenStream,
rules: &[MacroRule],
) -> (Span, ErrorGuaranteed) {
debug!("failed to match macro");
let def_head_span = if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) {
psess.source_map().guess_head_span(def_span)
} else {
DUMMY_SP
};
// An error occurred, try the expansion again, tracking the expansion closely for better
// diagnostics.
let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
let try_success_result = try_match_macro(psess, name, &arg, rules, &mut tracker);
let try_success_result = if let Some(attr_args) = attr_args {
try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
} else {
try_match_macro(psess, name, body, rules, &mut tracker)
};
if try_success_result.is_ok() {
// Nonterminal parser recovery might turn failed matches into successful ones,
@ -47,6 +58,18 @@ pub(super) fn failed_to_match_macro(
let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure
else {
// FIXME: we should report this at macro resolution time, as we do for
// `resolve_macro_cannot_use_as_attr`. We can do that once we track multiple macro kinds for a
// Def.
if attr_args.is_none() && !rules.iter().any(|rule| matches!(rule, MacroRule::Func { .. })) {
let msg = format!("macro has no rules for function-like invocation `{name}!`");
let mut err = psess.dcx().struct_span_err(sp, msg);
if !def_head_span.is_dummy() {
let msg = "this macro has no rules for function-like invocation";
err.span_label(def_head_span, msg);
}
return (sp, err.emit());
}
return (sp, psess.dcx().span_delayed_bug(sp, "failed to match a macro"));
};
@ -54,8 +77,8 @@ pub(super) fn failed_to_match_macro(
let mut err = psess.dcx().struct_span_err(span, parse_failure_msg(&token, None));
err.span_label(span, label);
if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) {
err.span_label(psess.source_map().guess_head_span(def_span), "when calling this macro");
if !def_head_span.is_dummy() {
err.span_label(def_head_span, "when calling this macro");
}
annotate_doc_comment(&mut err, psess.source_map(), span);
@ -79,13 +102,16 @@ pub(super) fn failed_to_match_macro(
}
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
if let Some((arg, comma_span)) = arg.add_comma() {
if attr_args.is_none()
&& let Some((body, comma_span)) = body.add_comma()
{
for rule in rules {
let parser = parser_from_cx(psess, arg.clone(), Recovery::Allowed);
let MacroRule::Func { lhs, .. } = rule else { continue };
let parser = parser_from_cx(psess, body.clone(), Recovery::Allowed);
let mut tt_parser = TtParser::new(name);
if let Success(_) =
tt_parser.parse_tt(&mut Cow::Borrowed(&parser), &rule.lhs, &mut NoopTracker)
tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker)
{
if comma_span.is_dummy() {
err.note("you might be missing a comma");
@ -116,13 +142,13 @@ struct CollectTrackerAndEmitter<'dcx, 'matcher> {
struct BestFailure {
token: Token,
position_in_tokenstream: u32,
position_in_tokenstream: (bool, u32),
msg: &'static str,
remaining_matcher: MatcherLoc,
}
impl BestFailure {
fn is_better_position(&self, position: u32) -> bool {
fn is_better_position(&self, position: (bool, u32)) -> bool {
position > self.position_in_tokenstream
}
}
@ -142,7 +168,7 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
}
}
fn after_arm(&mut self, result: &NamedParseResult<Self::Failure>) {
fn after_arm(&mut self, in_body: bool, result: &NamedParseResult<Self::Failure>) {
match result {
Success(_) => {
// Nonterminal parser recovery might turn failed matches into successful ones,
@ -155,14 +181,15 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
Failure((token, approx_position, msg)) => {
debug!(?token, ?msg, "a new failure of an arm");
let position_in_tokenstream = (in_body, *approx_position);
if self
.best_failure
.as_ref()
.is_none_or(|failure| failure.is_better_position(*approx_position))
.is_none_or(|failure| failure.is_better_position(position_in_tokenstream))
{
self.best_failure = Some(BestFailure {
token: *token,
position_in_tokenstream: *approx_position,
position_in_tokenstream,
msg,
remaining_matcher: self
.remaining_matcher

View file

@ -193,15 +193,19 @@ struct MacroState<'a> {
/// Arguments:
/// - `psess` is used to emit diagnostics and lints
/// - `node_id` is used to emit lints
/// - `lhs` and `rhs` represent the rule
/// - `args`, `lhs`, and `rhs` represent the rule
pub(super) fn check_meta_variables(
psess: &ParseSess,
node_id: NodeId,
args: Option<&TokenTree>,
lhs: &TokenTree,
rhs: &TokenTree,
) -> Result<(), ErrorGuaranteed> {
let mut guar = None;
let mut binders = Binders::default();
if let Some(args) = args {
check_binders(psess, node_id, args, &Stack::Empty, &mut binders, &Stack::Empty, &mut guar);
}
check_binders(psess, node_id, lhs, &Stack::Empty, &mut binders, &Stack::Empty, &mut guar);
check_occurrences(psess, node_id, rhs, &Stack::Empty, &binders, &Stack::Empty, &mut guar);
guar.map_or(Ok(()), Err)

View file

@ -6,12 +6,12 @@ use std::{mem, slice};
use ast::token::IdentIsRaw;
use rustc_ast::token::NtPatKind::*;
use rustc_ast::token::TokenKind::*;
use rustc_ast::token::{self, NonterminalKind, Token, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
use rustc_ast::tokenstream::{self, DelimSpan, TokenStream};
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
use rustc_feature::Features;
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
@ -23,23 +23,26 @@ use rustc_lint_defs::builtin::{
use rustc_parse::exp;
use rustc_parse::parser::{Parser, Recovery};
use rustc_session::Session;
use rustc_session::parse::ParseSess;
use rustc_session::parse::{ParseSess, feature_err};
use rustc_span::edition::Edition;
use rustc_span::hygiene::Transparency;
use rustc_span::{Ident, Span, kw, sym};
use tracing::{debug, instrument, trace, trace_span};
use super::diagnostics::failed_to_match_macro;
use super::macro_parser::{NamedMatches, NamedParseResult};
use super::{SequenceRepetition, diagnostics};
use crate::base::{
DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult, SyntaxExtension,
SyntaxExtensionKind, TTMacroExpander,
AttrProcMacro, DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult,
SyntaxExtension, SyntaxExtensionKind, TTMacroExpander,
};
use crate::errors;
use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_ast_fragment};
use crate::mbe::macro_check::check_meta_variables;
use crate::mbe::macro_parser::{Error, ErrorReported, Failure, MatcherLoc, Success, TtParser};
use crate::mbe::quoted::{RulePart, parse_one_tt};
use crate::mbe::transcribe::transcribe;
use crate::mbe::{self, KleeneOp, macro_check};
use crate::mbe::{self, KleeneOp};
pub(crate) struct ParserAnyMacro<'a> {
parser: Parser<'a>,
@ -123,10 +126,17 @@ impl<'a> ParserAnyMacro<'a> {
}
}
pub(super) struct MacroRule {
pub(super) lhs: Vec<MatcherLoc>,
lhs_span: Span,
rhs: mbe::TokenTree,
pub(super) enum MacroRule {
/// A function-style rule, for use with `m!()`
Func { lhs: Vec<MatcherLoc>, lhs_span: Span, rhs: mbe::TokenTree },
/// An attr rule, for use with `#[m]`
Attr {
args: Vec<MatcherLoc>,
args_span: Span,
body: Vec<MatcherLoc>,
body_span: Span,
rhs: mbe::TokenTree,
},
}
pub struct MacroRulesMacroExpander {
@ -138,10 +148,15 @@ pub struct MacroRulesMacroExpander {
}
impl MacroRulesMacroExpander {
pub fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, Span)> {
pub fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, MultiSpan)> {
// If the rhs contains an invocation like `compile_error!`, don't report it as unused.
let rule = &self.rules[rule_i];
if has_compile_error_macro(&rule.rhs) { None } else { Some((&self.name, rule.lhs_span)) }
let (span, rhs) = match self.rules[rule_i] {
MacroRule::Func { lhs_span, ref rhs, .. } => (MultiSpan::from_span(lhs_span), rhs),
MacroRule::Attr { args_span, body_span, ref rhs, .. } => {
(MultiSpan::from_spans(vec![args_span, body_span]), rhs)
}
};
if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) }
}
}
@ -165,6 +180,28 @@ impl TTMacroExpander for MacroRulesMacroExpander {
}
}
impl AttrProcMacro for MacroRulesMacroExpander {
fn expand(
&self,
cx: &mut ExtCtxt<'_>,
sp: Span,
args: TokenStream,
body: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
expand_macro_attr(
cx,
sp,
self.span,
self.node_id,
self.name,
self.transparency,
args,
body,
&self.rules,
)
}
}
struct DummyExpander(ErrorGuaranteed);
impl TTMacroExpander for DummyExpander {
@ -197,7 +234,7 @@ pub(super) trait Tracker<'matcher> {
/// This is called after an arm has been parsed, either successfully or unsuccessfully. When
/// this is called, `before_match_loc` was called at least once (with a `MatcherLoc::Eof`).
fn after_arm(&mut self, _result: &NamedParseResult<Self::Failure>) {}
fn after_arm(&mut self, _in_body: bool, _result: &NamedParseResult<Self::Failure>) {}
/// For tracing.
fn description() -> &'static str;
@ -245,14 +282,17 @@ fn expand_macro<'cx>(
match try_success_result {
Ok((rule_index, rule, named_matches)) => {
let mbe::TokenTree::Delimited(rhs_span, _, ref rhs) = rule.rhs else {
let MacroRule::Func { rhs, .. } = rule else {
panic!("try_match_macro returned non-func rule");
};
let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
cx.dcx().span_bug(sp, "malformed macro rhs");
};
let arm_span = rule.rhs.span();
let arm_span = rhs_span.entire();
// rhs has holes ( `$id` and `$(...)` that need filled)
let id = cx.current_expansion.id;
let tts = match transcribe(psess, &named_matches, rhs, rhs_span, transparency, id) {
let tts = match transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id) {
Ok(tts) => tts,
Err(err) => {
let guar = err.emit();
@ -280,13 +320,76 @@ fn expand_macro<'cx>(
Err(CanRetry::Yes) => {
// Retry and emit a better error.
let (span, guar) =
diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, rules);
failed_to_match_macro(cx.psess(), sp, def_span, name, None, &arg, rules);
cx.macro_error_and_trace_macros_diag();
DummyResult::any(span, guar)
}
}
}
/// Expands the rules based macro defined by `rules` for a given attribute `args` and `body`.
#[instrument(skip(cx, transparency, args, body, rules))]
fn expand_macro_attr(
cx: &mut ExtCtxt<'_>,
sp: Span,
def_span: Span,
node_id: NodeId,
name: Ident,
transparency: Transparency,
args: TokenStream,
body: TokenStream,
rules: &[MacroRule],
) -> Result<TokenStream, ErrorGuaranteed> {
let psess = &cx.sess.psess;
// Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id.
let is_local = node_id != DUMMY_NODE_ID;
if cx.trace_macros() {
let msg = format!(
"expanding `$[{name}({})] {}`",
pprust::tts_to_string(&args),
pprust::tts_to_string(&body),
);
trace_macros_note(&mut cx.expansions, sp, msg);
}
// Track nothing for the best performance.
match try_match_macro_attr(psess, name, &args, &body, rules, &mut NoopTracker) {
Ok((i, rule, named_matches)) => {
let MacroRule::Attr { rhs, .. } = rule else {
panic!("try_macro_match_attr returned non-attr rule");
};
let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
cx.dcx().span_bug(sp, "malformed macro rhs");
};
let id = cx.current_expansion.id;
let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id)
.map_err(|e| e.emit())?;
if cx.trace_macros() {
let msg = format!("to `{}`", pprust::tts_to_string(&tts));
trace_macros_note(&mut cx.expansions, sp, msg);
}
if is_local {
cx.resolver.record_macro_rule_usage(node_id, i);
}
Ok(tts)
}
Err(CanRetry::No(guar)) => Err(guar),
Err(CanRetry::Yes) => {
// Retry and emit a better error.
let (_, guar) =
failed_to_match_macro(cx.psess(), sp, def_span, name, Some(&args), &body, rules);
cx.trace_macros_diag();
Err(guar)
}
}
}
pub(super) enum CanRetry {
Yes,
/// We are not allowed to retry macro expansion as a fatal error has been emitted already.
@ -327,6 +430,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
// Try each arm's matchers.
let mut tt_parser = TtParser::new(name);
for (i, rule) in rules.iter().enumerate() {
let MacroRule::Func { lhs, .. } = rule else { continue };
let _tracing_span = trace_span!("Matching arm", %i);
// Take a snapshot of the state of pre-expansion gating at this point.
@ -335,9 +439,9 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut());
let result = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), &rule.lhs, track);
let result = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, track);
track.after_arm(&result);
track.after_arm(true, &result);
match result {
Success(named_matches) => {
@ -372,6 +476,60 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
Err(CanRetry::Yes)
}
/// Try expanding the macro attribute. Returns the index of the successful arm and its
/// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job
/// to use `track` accordingly to record all errors correctly.
#[instrument(level = "debug", skip(psess, attr_args, attr_body, rules, track), fields(tracking = %T::description()))]
pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
psess: &ParseSess,
name: Ident,
attr_args: &TokenStream,
attr_body: &TokenStream,
rules: &'matcher [MacroRule],
track: &mut T,
) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> {
// This uses the same strategy as `try_match_macro`
let args_parser = parser_from_cx(psess, attr_args.clone(), T::recovery());
let body_parser = parser_from_cx(psess, attr_body.clone(), T::recovery());
let mut tt_parser = TtParser::new(name);
for (i, rule) in rules.iter().enumerate() {
let MacroRule::Attr { args, body, .. } = rule else { continue };
let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut());
let result = tt_parser.parse_tt(&mut Cow::Borrowed(&args_parser), args, track);
track.after_arm(false, &result);
let mut named_matches = match result {
Success(named_matches) => named_matches,
Failure(_) => {
mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut());
continue;
}
Error(_, _) => return Err(CanRetry::Yes),
ErrorReported(guar) => return Err(CanRetry::No(guar)),
};
let result = tt_parser.parse_tt(&mut Cow::Borrowed(&body_parser), body, track);
track.after_arm(true, &result);
match result {
Success(body_named_matches) => {
psess.gated_spans.merge(gated_spans_snapshot);
named_matches.extend(body_named_matches);
return Ok((i, rule, named_matches));
}
Failure(_) => {
mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut())
}
Error(_, _) => return Err(CanRetry::Yes),
ErrorReported(guar) => return Err(CanRetry::No(guar)),
}
}
Err(CanRetry::Yes)
}
/// Converts a macro item into a syntax extension.
pub fn compile_declarative_macro(
sess: &Session,
@ -382,13 +540,13 @@ pub fn compile_declarative_macro(
span: Span,
node_id: NodeId,
edition: Edition,
) -> (SyntaxExtension, usize) {
let mk_syn_ext = |expander| {
let kind = SyntaxExtensionKind::LegacyBang(expander);
) -> (SyntaxExtension, Option<Arc<SyntaxExtension>>, usize) {
let mk_syn_ext = |kind| {
let is_local = is_defined_in_current_crate(node_id);
SyntaxExtension::new(sess, kind, span, Vec::new(), edition, ident.name, attrs, is_local)
};
let dummy_syn_ext = |guar| (mk_syn_ext(Arc::new(DummyExpander(guar))), 0);
let mk_bang_ext = |expander| mk_syn_ext(SyntaxExtensionKind::LegacyBang(expander));
let dummy_syn_ext = |guar| (mk_bang_ext(Arc::new(DummyExpander(guar))), None, 0);
let macro_rules = macro_def.macro_rules;
let exp_sep = if macro_rules { exp!(Semi) } else { exp!(Comma) };
@ -401,9 +559,30 @@ pub fn compile_declarative_macro(
let mut guar = None;
let mut check_emission = |ret: Result<(), ErrorGuaranteed>| guar = guar.or(ret.err());
let mut has_attr_rules = false;
let mut rules = Vec::new();
while p.token != token::Eof {
let args = if p.eat_keyword_noexpect(sym::attr) {
has_attr_rules = true;
if !features.macro_attr() {
feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable")
.emit();
}
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr args") {
return dummy_syn_ext(guar);
}
let args = p.parse_token_tree();
check_args_parens(sess, &args);
let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition);
check_emission(check_lhs(sess, node_id, &args));
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") {
return dummy_syn_ext(guar);
}
Some(args)
} else {
None
};
let lhs_tt = p.parse_token_tree();
let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
check_emission(check_lhs(sess, node_id, &lhs_tt));
@ -416,7 +595,7 @@ pub fn compile_declarative_macro(
let rhs_tt = p.parse_token_tree();
let rhs_tt = parse_one_tt(rhs_tt, RulePart::Body, sess, node_id, features, edition);
check_emission(check_rhs(sess, &rhs_tt));
check_emission(macro_check::check_meta_variables(&sess.psess, node_id, &lhs_tt, &rhs_tt));
check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs_tt));
let lhs_span = lhs_tt.span();
// Convert the lhs into `MatcherLoc` form, which is better for doing the
// actual matching.
@ -425,7 +604,17 @@ pub fn compile_declarative_macro(
} else {
return dummy_syn_ext(guar.unwrap());
};
rules.push(MacroRule { lhs, lhs_span, rhs: rhs_tt });
if let Some(args) = args {
let args_span = args.span();
let mbe::TokenTree::Delimited(.., delimited) = args else {
return dummy_syn_ext(guar.unwrap());
};
let args = mbe::macro_parser::compute_locs(&delimited.tts);
let body_span = lhs_span;
rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
} else {
rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
}
if p.token == token::Eof {
break;
}
@ -451,9 +640,12 @@ pub fn compile_declarative_macro(
// Return the number of rules for unused rule linting, if this is a local macro.
let nrules = if is_defined_in_current_crate(node_id) { rules.len() } else { 0 };
let expander =
Arc::new(MacroRulesMacroExpander { name: ident, span, node_id, transparency, rules });
(mk_syn_ext(expander), nrules)
let exp = Arc::new(MacroRulesMacroExpander { name: ident, span, node_id, transparency, rules });
let opt_attr_ext = has_attr_rules.then(|| {
let exp = Arc::clone(&exp);
Arc::new(mk_syn_ext(SyntaxExtensionKind::Attr(exp)))
});
(mk_bang_ext(exp), opt_attr_ext, nrules)
}
fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<ErrorGuaranteed> {
@ -469,6 +661,18 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
None
}
fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) {
// This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
if let tokenstream::TokenTree::Delimited(dspan, _, delim, _) = args
&& *delim != Delimiter::Parenthesis
{
sess.dcx().emit_err(errors::MacroArgsBadDelim {
span: dspan.entire(),
sugg: errors::MacroArgsBadDelimSugg { open: dspan.open, close: dspan.close },
});
}
}
fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs));

View file

@ -54,7 +54,7 @@ declare_features! (
/// Allows using the `amdgpu-kernel` ABI.
(removed, abi_amdgpu_kernel, "1.77.0", Some(51575), None, 120495),
(removed, abi_c_cmse_nonsecure_call, "CURRENT_RUSTC_VERSION", Some(81391), Some("renamed to abi_cmse_nonsecure_call"), 142146),
(removed, abi_c_cmse_nonsecure_call, "1.90.0", Some(81391), Some("renamed to abi_cmse_nonsecure_call"), 142146),
(removed, advanced_slice_patterns, "1.42.0", Some(62254),
Some("merged into `#![feature(slice_patterns)]`"), 67712),
(removed, allocator, "1.0.0", None, None),
@ -300,7 +300,7 @@ declare_features! (
// FIXME(#141617): we should have a better way to track removed library features, but we reuse
// the infrastructure here so users still get hints. The symbols used here can be remove from
// `symbol.rs` when that happens.
(removed, concat_idents, "CURRENT_RUSTC_VERSION", Some(29599),
(removed, concat_idents, "1.90.0", Some(29599),
Some("use the `${concat(..)}` metavariable expression instead"), 142704),
// -------------------------------------------------------------------------
// feature-group-end: removed library features

View file

@ -327,6 +327,7 @@ declare_features! (
(unstable, m68k_target_feature, "1.85.0", Some(134328)),
(unstable, mips_target_feature, "1.27.0", Some(44839)),
(unstable, movrs_target_feature, "1.88.0", Some(137976)),
(unstable, nvptx_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
(unstable, powerpc_target_feature, "1.27.0", Some(44839)),
(unstable, prfchw_target_feature, "1.78.0", Some(44839)),
(unstable, riscv_target_feature, "1.45.0", Some(44839)),
@ -352,7 +353,7 @@ declare_features! (
/// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`.
(unstable, abi_avr_interrupt, "1.45.0", Some(69664)),
/// Allows `extern "cmse-nonsecure-call" fn()`.
(unstable, abi_cmse_nonsecure_call, "CURRENT_RUSTC_VERSION", Some(81391)),
(unstable, abi_cmse_nonsecure_call, "1.90.0", Some(81391)),
/// Allows `extern "custom" fn()`.
(unstable, abi_custom, "1.89.0", Some(140829)),
/// Allows `extern "gpu-kernel" fn()`.
@ -552,7 +553,9 @@ declare_features! (
/// to pass custom arguments to the linker.
(unstable, link_arg_attribute, "1.76.0", Some(99427)),
/// Allows fused `loop`/`match` for direct intraprocedural jumps.
(incomplete, loop_match, "CURRENT_RUSTC_VERSION", Some(132306)),
(incomplete, loop_match, "1.90.0", Some(132306)),
/// Allow `macro_rules!` attribute rules
(unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)),
/// Give access to additional metadata about declarative macro meta-variables.
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
/// Provides a way to concatenate identifiers using metavariable expressions.

View file

@ -297,6 +297,9 @@ pub enum AttributeKind {
/// Represents `#[const_trait]`.
ConstTrait(Span),
/// Represents `#[coroutine]`.
Coroutine(Span),
/// Represents `#[coverage(..)]`.
Coverage(Span, CoverageAttrKind),
@ -433,6 +436,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,
/// Represents `#[should_panic]`
ShouldPanic { reason: Option<Symbol>, span: Span },
/// Represents `#[rustc_skip_during_method_dispatch]`.
SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },

View file

@ -28,6 +28,7 @@ impl AttributeKind {
ConstStability { .. } => Yes,
ConstStabilityIndirect => No,
ConstTrait(..) => No,
Coroutine(..) => No,
Coverage(..) => No,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
@ -69,6 +70,7 @@ impl AttributeKind {
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcObjectLifetimeDefault => No,
ShouldPanic { .. } => No,
SkipDuringMethodDispatch { .. } => No,
SpecializationTrait(..) => No,
Stability { .. } => Yes,

View file

@ -1308,6 +1308,7 @@ impl AttributeExt for Attribute {
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span,
Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
Attribute::Parsed(AttributeKind::ShouldPanic { span, .. }) => *span,
Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span,
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
}

View file

@ -652,16 +652,16 @@ pub(crate) fn check_intrinsic_type(
sym::atomic_store => (1, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit),
sym::atomic_xchg
| sym::atomic_xadd
| sym::atomic_xsub
| sym::atomic_and
| sym::atomic_nand
| sym::atomic_or
| sym::atomic_xor
| sym::atomic_max
| sym::atomic_min
| sym::atomic_umax
| sym::atomic_umin => (1, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)),
sym::atomic_xadd
| sym::atomic_xsub
| sym::atomic_and
| sym::atomic_nand
| sym::atomic_or
| sym::atomic_xor => (2, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(1)], param(0)),
sym::atomic_fence | sym::atomic_singlethreadfence => (0, 1, Vec::new(), tcx.types.unit),
other => {

View file

@ -223,60 +223,27 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
"synthetic HIR should have its `generics_of` explicitly fed"
),
_ => span_bug!(tcx.def_span(def_id), "unhandled node {node:?}"),
_ => span_bug!(tcx.def_span(def_id), "generics_of: unexpected node kind {node:?}"),
};
enum Defaults {
Allowed,
// See #36887
FutureCompatDisallowed,
Deny,
}
// Add in the self type parameter.
let opt_self = if let Node::Item(item) = node
&& let ItemKind::Trait(..) | ItemKind::TraitAlias(..) = item.kind
{
// Something of a hack: We reuse the node ID of the trait for the self type parameter.
Some(ty::GenericParamDef {
index: 0,
name: kw::SelfUpper,
def_id: def_id.to_def_id(),
pure_wrt_drop: false,
kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },
})
} else {
None
};
let param_default_policy = param_default_policy(node);
let hir_generics = node.generics().unwrap_or(hir::Generics::empty());
let (opt_self, allow_defaults) = match node {
Node::Item(item) => {
match item.kind {
ItemKind::Trait(..) | ItemKind::TraitAlias(..) => {
// Add in the self type parameter.
//
// Something of a hack: use the node id for the trait, also as
// the node id for the Self type parameter.
let opt_self = Some(ty::GenericParamDef {
index: 0,
name: kw::SelfUpper,
def_id: def_id.to_def_id(),
pure_wrt_drop: false,
kind: ty::GenericParamDefKind::Type {
has_default: false,
synthetic: false,
},
});
(opt_self, Defaults::Allowed)
}
ItemKind::TyAlias(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..) => (None, Defaults::Allowed),
ItemKind::Const(..) => (None, Defaults::Deny),
_ => (None, Defaults::FutureCompatDisallowed),
}
}
Node::OpaqueTy(..) => (None, Defaults::Allowed),
// GATs
Node::TraitItem(item) if matches!(item.kind, TraitItemKind::Type(..)) => {
(None, Defaults::Deny)
}
Node::ImplItem(item) if matches!(item.kind, ImplItemKind::Type(..)) => {
(None, Defaults::Deny)
}
_ => (None, Defaults::FutureCompatDisallowed),
};
let has_self = opt_self.is_some();
let mut parent_has_self = false;
let mut own_start = has_self as u32;
@ -312,60 +279,53 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
prev + type_start
};
const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
`struct`, `enum`, `type`, or `trait` definitions";
own_params.extend(hir_generics.params.iter().filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => None,
GenericParamKind::Type { default, synthetic, .. } => {
if default.is_some() {
match allow_defaults {
Defaults::Allowed => {}
Defaults::FutureCompatDisallowed => {
tcx.node_span_lint(
lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
param.hir_id,
param.span,
|lint| {
lint.primary_message(TYPE_DEFAULT_NOT_ALLOWED);
},
);
}
Defaults::Deny => {
tcx.dcx().span_err(param.span, TYPE_DEFAULT_NOT_ALLOWED);
own_params.extend(hir_generics.params.iter().filter_map(|param| {
const MESSAGE: &str = "defaults for generic parameters are not allowed here";
let kind = match param.kind {
GenericParamKind::Lifetime { .. } => return None,
GenericParamKind::Type { default, synthetic } => {
if default.is_some() {
match param_default_policy.expect("no policy for generic param default") {
ParamDefaultPolicy::Allowed => {}
ParamDefaultPolicy::FutureCompatForbidden => {
tcx.node_span_lint(
lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
param.hir_id,
param.span,
|lint| {
lint.primary_message(MESSAGE);
},
);
}
ParamDefaultPolicy::Forbidden => {
tcx.dcx().span_err(param.span, MESSAGE);
}
}
}
ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic }
}
GenericParamKind::Const { ty: _, default, synthetic } => {
if default.is_some() {
match param_default_policy.expect("no policy for generic param default") {
ParamDefaultPolicy::Allowed => {}
ParamDefaultPolicy::FutureCompatForbidden
| ParamDefaultPolicy::Forbidden => {
tcx.dcx().span_err(param.span, MESSAGE);
}
}
}
let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic };
Some(ty::GenericParamDef {
index: next_index(),
name: param.name.ident().name,
def_id: param.def_id.to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
kind,
})
}
GenericParamKind::Const { ty: _, default, synthetic } => {
if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
tcx.dcx().span_err(
param.span,
"defaults for const parameters are only allowed in \
`struct`, `enum`, `type`, or `trait` definitions",
);
ty::GenericParamDefKind::Const { has_default: default.is_some(), synthetic }
}
let index = next_index();
Some(ty::GenericParamDef {
index,
name: param.name.ident().name,
def_id: param.def_id.to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
kind: ty::GenericParamDefKind::Const { has_default: default.is_some(), synthetic },
})
}
};
Some(ty::GenericParamDef {
index: next_index(),
name: param.name.ident().name,
def_id: param.def_id.to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
kind,
})
}));
// provide junk type parameter defs - the only place that
@ -438,6 +398,48 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
}
}
#[derive(Clone, Copy)]
enum ParamDefaultPolicy {
Allowed,
/// Tracked in <https://github.com/rust-lang/rust/issues/36887>.
FutureCompatForbidden,
Forbidden,
}
fn param_default_policy(node: Node<'_>) -> Option<ParamDefaultPolicy> {
use rustc_hir::*;
Some(match node {
Node::Item(item) => match item.kind {
ItemKind::Trait(..)
| ItemKind::TraitAlias(..)
| ItemKind::TyAlias(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..) => ParamDefaultPolicy::Allowed,
ItemKind::Fn { .. } | ItemKind::Impl(_) => ParamDefaultPolicy::FutureCompatForbidden,
// Re. GCI, we're not bound by backward compatibility.
ItemKind::Const(..) => ParamDefaultPolicy::Forbidden,
_ => return None,
},
Node::TraitItem(item) => match item.kind {
// Re. GATs and GACs (generic_const_items), we're not bound by backward compatibility.
TraitItemKind::Const(..) | TraitItemKind::Type(..) => ParamDefaultPolicy::Forbidden,
TraitItemKind::Fn(..) => ParamDefaultPolicy::FutureCompatForbidden,
},
Node::ImplItem(item) => match item.kind {
// Re. GATs and GACs (generic_const_items), we're not bound by backward compatibility.
ImplItemKind::Const(..) | ImplItemKind::Type(..) => ParamDefaultPolicy::Forbidden,
ImplItemKind::Fn(..) => ParamDefaultPolicy::FutureCompatForbidden,
},
// Generic params are (semantically) invalid on foreign items. Still, for maximum forward
// compatibility, let's hard-reject defaults on them.
Node::ForeignItem(_) => ParamDefaultPolicy::Forbidden,
Node::OpaqueTy(..) => ParamDefaultPolicy::Allowed,
_ => return None,
})
}
fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {
struct LateBoundRegionsDetector<'tcx> {
tcx: TyCtxt<'tcx>,

View file

@ -1135,9 +1135,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
}
} else {
let trait_ =
tcx.short_string(bound.print_only_trait_path(), err.long_ty_path());
err.note(format!(
"associated {assoc_kind_str} `{assoc_ident}` could derive from `{}`",
bound.print_only_trait_path(),
"associated {assoc_kind_str} `{assoc_ident}` could derive from `{trait_}`",
));
}
}

View file

@ -159,7 +159,7 @@ hir_typeck_lossy_provenance_ptr2int =
.suggestion = use `.addr()` to obtain the address of a pointer
.help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead
hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}`
hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty}`
hir_typeck_naked_asm_outside_naked_fn =
the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
@ -184,7 +184,7 @@ hir_typeck_never_type_fallback_flowing_into_unsafe_path = never type fallback af
hir_typeck_never_type_fallback_flowing_into_unsafe_union_field = never type fallback affects this union access
.help = specify the type explicitly
hir_typeck_no_associated_item = no {$item_kind} named `{$item_ident}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method ->
hir_typeck_no_associated_item = no {$item_kind} named `{$item_ident}` found for {$ty_prefix} `{$ty}`{$trait_missing_method ->
[true] {""}
*[other] {" "}in the current scope
}

View file

@ -200,11 +200,11 @@ pub(crate) enum ExplicitDestructorCallSugg {
#[derive(Diagnostic)]
#[diag(hir_typeck_missing_parentheses_in_range, code = E0689)]
pub(crate) struct MissingParenthesesInRange {
pub(crate) struct MissingParenthesesInRange<'tcx> {
#[primary_span]
#[label(hir_typeck_missing_parentheses_in_range)]
pub span: Span,
pub ty_str: String,
pub ty: Ty<'tcx>,
pub method_name: String,
#[subdiagnostic]
pub add_missing_parentheses: Option<AddMissingParenthesesInRange>,
@ -828,13 +828,13 @@ pub(crate) struct UnlabeledCfInWhileCondition<'a> {
#[derive(Diagnostic)]
#[diag(hir_typeck_no_associated_item, code = E0599)]
pub(crate) struct NoAssociatedItem {
pub(crate) struct NoAssociatedItem<'tcx> {
#[primary_span]
pub span: Span,
pub item_kind: &'static str,
pub item_ident: Ident,
pub ty_prefix: Cow<'static, str>,
pub ty_str: String,
pub ty: Ty<'tcx>,
pub trait_missing_method: bool,
}

View file

@ -2745,6 +2745,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let available_field_names = self.available_field_names(variant, expr, skip_fields);
if let Some(field_name) =
find_best_match_for_name(&available_field_names, field.ident.name, None)
&& !(field.ident.name.as_str().parse::<usize>().is_ok()
&& field_name.as_str().parse::<usize>().is_ok())
{
err.span_label(field.ident.span, "unknown field");
err.span_suggestion_verbose(
@ -3321,18 +3323,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
(base_ty, "")
};
for (found_fields, args) in
for found_fields in
self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id)
{
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
let field_names = found_fields.iter().map(|field| field.0.name).collect::<Vec<_>>();
let mut candidate_fields: Vec<_> = found_fields
.into_iter()
.filter_map(|candidate_field| {
self.check_for_nested_field_satisfying_condition_for_diag(
span,
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
&|candidate_field, _| candidate_field == field,
candidate_field,
args,
vec![],
mod_id,
expr.hir_id,
@ -3361,6 +3362,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
} else if let Some(field_name) =
find_best_match_for_name(&field_names, field.name, None)
&& !(field.name.as_str().parse::<usize>().is_ok()
&& field_name.as_str().parse::<usize>().is_ok())
{
err.span_suggestion_verbose(
field.span,
@ -3396,7 +3399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
base_ty: Ty<'tcx>,
mod_id: DefId,
hir_id: HirId,
) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> {
) -> Vec<Vec<(Ident, Ty<'tcx>)>> {
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);
let mut autoderef = self.autoderef(span, base_ty).silence_errors();
@ -3422,7 +3425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
return None;
}
return Some((
return Some(
fields
.iter()
.filter(move |field| {
@ -3431,9 +3434,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
// For compile-time reasons put a limit on number of fields we search
.take(100)
.map(|field_def| {
(
field_def.ident(self.tcx).normalize_to_macros_2_0(),
field_def.ty(self.tcx, args),
)
})
.collect::<Vec<_>>(),
*args,
));
);
}
ty::Tuple(types) => {
return Some(
types
.iter()
.enumerate()
// For compile-time reasons put a limit on number of fields we search
.take(100)
.map(|(i, ty)| (Ident::from_str(&i.to_string()), ty))
.collect::<Vec<_>>(),
);
}
_ => None,
}
@ -3443,56 +3462,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// This method is called after we have encountered a missing field error to recursively
/// search for the field
#[instrument(skip(self, matches, mod_id, hir_id), level = "debug")]
pub(crate) fn check_for_nested_field_satisfying_condition_for_diag(
&self,
span: Span,
matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
candidate_field: &ty::FieldDef,
subst: GenericArgsRef<'tcx>,
matches: &impl Fn(Ident, Ty<'tcx>) -> bool,
(candidate_name, candidate_ty): (Ident, Ty<'tcx>),
mut field_path: Vec<Ident>,
mod_id: DefId,
hir_id: HirId,
) -> Option<Vec<Ident>> {
debug!(
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
span, candidate_field, field_path
);
if field_path.len() > 3 {
// For compile-time reasons and to avoid infinite recursion we only check for fields
// up to a depth of three
None
} else {
field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
let field_ty = candidate_field.ty(self.tcx, subst);
if matches(candidate_field, field_ty) {
return Some(field_path);
} else {
for (nested_fields, subst) in self
.get_field_candidates_considering_privacy_for_diag(
span, field_ty, mod_id, hir_id,
)
{
// recursively search fields of `candidate_field` if it's a ty::Adt
for field in nested_fields {
if let Some(field_path) = self
.check_for_nested_field_satisfying_condition_for_diag(
span,
matches,
field,
subst,
field_path.clone(),
mod_id,
hir_id,
)
{
return Some(field_path);
}
}
return None;
}
field_path.push(candidate_name);
if matches(candidate_name, candidate_ty) {
return Some(field_path);
}
for nested_fields in self.get_field_candidates_considering_privacy_for_diag(
span,
candidate_ty,
mod_id,
hir_id,
) {
// recursively search fields of `candidate_field` if it's a ty::Adt
for field in nested_fields {
if let Some(field_path) = self.check_for_nested_field_satisfying_condition_for_diag(
span,
matches,
field,
field_path.clone(),
mod_id,
hir_id,
) {
return Some(field_path);
}
}
None
}
None
}
fn check_expr_index(

View file

@ -376,16 +376,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
fn suggest_missing_writer(&self, rcvr_ty: Ty<'tcx>, rcvr_expr: &hir::Expr<'tcx>) -> Diag<'_> {
let mut file = None;
fn suggest_missing_writer(
&self,
rcvr_ty: Ty<'tcx>,
rcvr_expr: &hir::Expr<'tcx>,
mut long_ty_path: Option<PathBuf>,
) -> Diag<'_> {
let mut err = struct_span_code_err!(
self.dcx(),
rcvr_expr.span,
E0599,
"cannot write into `{}`",
self.tcx.short_string(rcvr_ty, &mut file),
self.tcx.short_string(rcvr_ty, &mut long_ty_path),
);
*err.long_ty_path() = file;
*err.long_ty_path() = long_ty_path;
err.span_note(
rcvr_expr.span,
"must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method",
@ -403,7 +407,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
self_source: SelfSource<'tcx>,
method_name: Ident,
ty_str_reported: &str,
ty: Ty<'tcx>,
err: &mut Diag<'_>,
) {
#[derive(Debug)]
@ -478,7 +482,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
// If the shadowed binding has an itializer expression,
// use the initializer expression'ty to try to find the method again.
// use the initializer expression's ty to try to find the method again.
// For example like: `let mut x = Vec::new();`,
// `Vec::new()` is the itializer expression.
if let Some(self_ty) = self.fcx.node_ty_opt(binding.init_hir_id)
@ -566,17 +570,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut span = MultiSpan::from_span(sugg_let.span);
span.push_span_label(sugg_let.span,
format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here"));
let ty = self.tcx.short_string(ty, err.long_ty_path());
span.push_span_label(
self.tcx.hir_span(recv_id),
format!(
"earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`"
),
format!("earlier `{rcvr_name}` shadowed here with type `{ty}`"),
);
err.span_note(
span,
format!(
"there's an earlier shadowed binding `{rcvr_name}` of type `{self_ty}` \
that has method `{method_name}` available"
that has method `{method_name}` available"
),
);
}
@ -602,15 +606,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
let mut ty_file = None;
let (ty_str, short_ty_str) =
if trait_missing_method && let ty::Dynamic(predicates, _, _) = rcvr_ty.kind() {
(predicates.to_string(), with_forced_trimmed_paths!(predicates.to_string()))
} else {
(
tcx.short_string(rcvr_ty, &mut ty_file),
with_forced_trimmed_paths!(rcvr_ty.to_string()),
)
};
let is_method = mode == Mode::MethodCall;
let unsatisfied_predicates = &no_match_data.unsatisfied_predicates;
let similar_candidate = no_match_data.similar_candidate;
@ -629,15 +624,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We could pass the file for long types into these two, but it isn't strictly necessary
// given how targeted they are.
if let Err(guar) = self.report_failed_method_call_on_range_end(
tcx,
rcvr_ty,
source,
span,
item_ident,
&short_ty_str,
&mut ty_file,
) {
if let Err(guar) =
self.report_failed_method_call_on_range_end(tcx, rcvr_ty, source, span, item_ident)
{
return guar;
}
if let Err(guar) = self.report_failed_method_call_on_numerical_infer_var(
@ -647,44 +636,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
item_kind,
item_ident,
&short_ty_str,
&mut ty_file,
) {
return guar;
}
span = item_ident.span;
// Don't show generic arguments when the method can't be found in any implementation (#81576).
let mut ty_str_reported = ty_str.clone();
if let ty::Adt(_, generics) = rcvr_ty.kind() {
if generics.len() > 0 {
let mut autoderef = self.autoderef(span, rcvr_ty).silence_errors();
let candidate_found = autoderef.any(|(ty, _)| {
if let ty::Adt(adt_def, _) = ty.kind() {
self.tcx
.inherent_impls(adt_def.did())
.into_iter()
.any(|def_id| self.associated_value(*def_id, item_ident).is_some())
} else {
false
}
});
let has_deref = autoderef.step_count() > 0;
if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() {
if let Some((path_string, _)) = ty_str.split_once('<') {
ty_str_reported = path_string.to_string();
}
}
}
}
let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.is_some_and(|def_id| {
tcx.is_diagnostic_item(sym::write_macro, def_id)
|| tcx.is_diagnostic_item(sym::writeln_macro, def_id)
}) && item_ident.name == sym::write_fmt;
let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source {
self.suggest_missing_writer(rcvr_ty, rcvr_expr)
self.suggest_missing_writer(rcvr_ty, rcvr_expr, ty_file)
} else {
// Don't show expanded generic arguments when the method can't be found in any
// implementation (#81576).
let mut ty = rcvr_ty;
if let ty::Adt(def, generics) = rcvr_ty.kind() {
if generics.len() > 0 {
let mut autoderef = self.autoderef(span, rcvr_ty).silence_errors();
let candidate_found = autoderef.any(|(ty, _)| {
if let ty::Adt(adt_def, _) = ty.kind() {
self.tcx
.inherent_impls(adt_def.did())
.into_iter()
.any(|def_id| self.associated_value(*def_id, item_ident).is_some())
} else {
false
}
});
let has_deref = autoderef.step_count() > 0;
if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() {
ty = self.tcx.at(span).type_of(def.did()).instantiate_identity();
}
}
}
let mut err = self.dcx().create_err(NoAssociatedItem {
span,
item_kind,
@ -695,16 +682,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
rcvr_ty.prefix_string(self.tcx)
},
ty_str: ty_str_reported.clone(),
ty,
trait_missing_method,
});
if is_method {
self.suggest_use_shadowed_binding_with_method(
source,
item_ident,
&ty_str_reported,
&mut err,
source, item_ident, rcvr_ty, &mut err,
);
}
@ -734,6 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err
};
if tcx.sess.source_map().is_multiline(sugg_span) {
err.span_label(sugg_span.with_hi(span.lo()), "");
}
@ -750,6 +735,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if tcx.ty_is_opaque_future(rcvr_ty) && item_ident.name == sym::poll {
let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
err.help(format!(
"method `poll` found on `Pin<&mut {ty_str}>`, \
see documentation for `std::pin::Pin`"
@ -1339,7 +1325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let OnUnimplementedNote { message, label, notes, .. } = self
.err_ctxt()
.on_unimplemented_note(trait_ref, &obligation, &mut ty_file);
.on_unimplemented_note(trait_ref, &obligation, err.long_ty_path());
(message, label, notes)
})
.unwrap()
@ -1347,6 +1333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(None, None, Vec::new())
};
let primary_message = primary_message.unwrap_or_else(|| {
let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
format!(
"the {item_kind} `{item_ident}` exists for {actual_prefix} `{ty_str}`, \
but its trait bounds were not satisfied"
@ -1409,6 +1396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut find_candidate_for_method = false;
let mut label_span_not_found = |err: &mut Diag<'_>| {
let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
if unsatisfied_predicates.is_empty() {
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
let is_string_or_ref_str = match rcvr_ty.kind() {
@ -2520,8 +2508,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
source: SelfSource<'tcx>,
span: Span,
item_name: Ident,
ty_str: &str,
long_ty_path: &mut Option<PathBuf>,
) -> Result<(), ErrorGuaranteed> {
if let SelfSource::MethodCall(expr) = source {
for (_, parent) in tcx.hir_parent_iter(expr.hir_id).take(5) {
@ -2583,18 +2569,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
if pick.is_ok() {
let range_span = parent_expr.span.with_hi(expr.span.hi());
let mut err = self.dcx().create_err(errors::MissingParenthesesInRange {
return Err(self.dcx().emit_err(errors::MissingParenthesesInRange {
span,
ty_str: ty_str.to_string(),
ty: actual,
method_name: item_name.as_str().to_string(),
add_missing_parentheses: Some(errors::AddMissingParenthesesInRange {
func_name: item_name.name.as_str().to_string(),
left: range_span.shrink_to_lo(),
right: range_span.shrink_to_hi(),
}),
});
*err.long_ty_path() = long_ty_path.take();
return Err(err.emit());
}));
}
}
}
@ -2610,7 +2594,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
item_kind: &str,
item_name: Ident,
ty_str: &str,
long_ty_path: &mut Option<PathBuf>,
) -> Result<(), ErrorGuaranteed> {
let found_candidate = all_traits(self.tcx)
@ -2643,14 +2626,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& !actual.has_concrete_skeleton()
&& let SelfSource::MethodCall(expr) = source
{
let ty_str = self.tcx.short_string(actual, long_ty_path);
let mut err = struct_span_code_err!(
self.dcx(),
span,
E0689,
"can't call {} `{}` on ambiguous numeric type `{}`",
item_kind,
item_name,
ty_str
"can't call {item_kind} `{item_name}` on ambiguous numeric type `{ty_str}`"
);
*err.long_ty_path() = long_ty_path.take();
let concrete_type = if actual.is_integral() { "i32" } else { "f32" };
@ -2792,7 +2773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
if let SelfSource::MethodCall(expr) = source {
let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id();
for (fields, args) in self.get_field_candidates_considering_privacy_for_diag(
for fields in self.get_field_candidates_considering_privacy_for_diag(
span,
actual,
mod_id,
@ -2831,7 +2812,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
},
candidate_field,
args,
vec![],
mod_id,
expr.hir_id,
@ -3671,7 +3651,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
{
debug!("try_alt_rcvr: pick candidate {:?}", pick);
let did = Some(pick.item.container_id(self.tcx));
let did = pick.item.trait_container(self.tcx);
// We don't want to suggest a container type when the missing
// method is `.clone()` or `.deref()` otherwise we'd suggest
// `Arc::new(foo).clone()`, which is far from what the user wants.
@ -3720,8 +3700,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& !alt_rcvr_sugg
// `T: !Unpin`
&& !unpin
// The method isn't `as_ref`, as it would provide a wrong suggestion for `Pin`.
&& sym::as_ref != item_name.name
// Either `Pin::as_ref` or `Pin::as_mut`.
&& let Some(pin_call) = pin_call
// Search for `item_name` as a method accessible on `Pin<T>`.
@ -3735,6 +3713,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We skip some common traits that we don't want to consider because autoderefs
// would take care of them.
&& !skippable.contains(&Some(pick.item.container_id(self.tcx)))
// Do not suggest pinning when the method is directly on `Pin`.
&& pick.item.impl_container(self.tcx).map_or(true, |did| {
match self.tcx.type_of(did).skip_binder().kind() {
ty::Adt(def, _) => Some(def.did()) != self.tcx.lang_items().pin_type(),
_ => true,
}
})
// We don't want to go through derefs.
&& pick.autoderefs == 0
// Check that the method of the same name that was found on the new `Pin<T>`

View file

@ -777,7 +777,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(
coverage_options,
CoverageOptions {
level: CoverageLevel::Mcdc,
level: CoverageLevel::Branch,
// (don't collapse test-only options onto the same line)
discard_all_spans_in_codegen: true,
}

View file

@ -434,7 +434,7 @@ fn emit_mismatch_diagnostic<'tcx>(
lints::MismatchedLifetimeSyntaxesSuggestion::Mixed {
implicit_suggestions,
explicit_anonymous_suggestions,
tool_only: false,
optional_alternative: false,
}
});
@ -455,7 +455,10 @@ fn emit_mismatch_diagnostic<'tcx>(
let implicit_suggestion = should_suggest_implicit.then(|| {
let suggestions = make_implicit_suggestions(&suggest_change_to_implicit);
lints::MismatchedLifetimeSyntaxesSuggestion::Implicit { suggestions, tool_only: false }
lints::MismatchedLifetimeSyntaxesSuggestion::Implicit {
suggestions,
optional_alternative: false,
}
});
tracing::debug!(
@ -508,7 +511,7 @@ fn build_mismatch_suggestion(
lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
lifetime_name,
suggestions,
tool_only: false,
optional_alternative: false,
}
}

View file

@ -3292,7 +3292,7 @@ impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSynta
diag.subdiagnostic(s);
for mut s in suggestions {
s.make_tool_only();
s.make_optional_alternative();
diag.subdiagnostic(s);
}
}
@ -3303,33 +3303,33 @@ impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSynta
pub(crate) enum MismatchedLifetimeSyntaxesSuggestion {
Implicit {
suggestions: Vec<Span>,
tool_only: bool,
optional_alternative: bool,
},
Mixed {
implicit_suggestions: Vec<Span>,
explicit_anonymous_suggestions: Vec<(Span, String)>,
tool_only: bool,
optional_alternative: bool,
},
Explicit {
lifetime_name: String,
suggestions: Vec<(Span, String)>,
tool_only: bool,
optional_alternative: bool,
},
}
impl MismatchedLifetimeSyntaxesSuggestion {
fn make_tool_only(&mut self) {
fn make_optional_alternative(&mut self) {
use MismatchedLifetimeSyntaxesSuggestion::*;
let tool_only = match self {
Implicit { tool_only, .. } | Mixed { tool_only, .. } | Explicit { tool_only, .. } => {
tool_only
}
let optional_alternative = match self {
Implicit { optional_alternative, .. }
| Mixed { optional_alternative, .. }
| Explicit { optional_alternative, .. } => optional_alternative,
};
*tool_only = true;
*optional_alternative = true;
}
}
@ -3337,22 +3337,40 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
use MismatchedLifetimeSyntaxesSuggestion::*;
let style = |tool_only| {
if tool_only { SuggestionStyle::CompletelyHidden } else { SuggestionStyle::ShowAlways }
let style = |optional_alternative| {
if optional_alternative {
SuggestionStyle::CompletelyHidden
} else {
SuggestionStyle::ShowAlways
}
};
let applicability = |optional_alternative| {
// `cargo fix` can't handle more than one fix for the same issue,
// so hide alternative suggestions from it by marking them as maybe-incorrect
if optional_alternative {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
}
};
match self {
Implicit { suggestions, tool_only } => {
Implicit { suggestions, optional_alternative } => {
let suggestions = suggestions.into_iter().map(|s| (s, String::new())).collect();
diag.multipart_suggestion_with_style(
fluent::lint_mismatched_lifetime_syntaxes_suggestion_implicit,
suggestions,
Applicability::MaybeIncorrect,
style(tool_only),
applicability(optional_alternative),
style(optional_alternative),
);
}
Mixed { implicit_suggestions, explicit_anonymous_suggestions, tool_only } => {
Mixed {
implicit_suggestions,
explicit_anonymous_suggestions,
optional_alternative,
} => {
let message = if implicit_suggestions.is_empty() {
fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths
} else {
@ -3368,12 +3386,12 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
diag.multipart_suggestion_with_style(
message,
suggestions,
Applicability::MaybeIncorrect,
style(tool_only),
applicability(optional_alternative),
style(optional_alternative),
);
}
Explicit { lifetime_name, suggestions, tool_only } => {
Explicit { lifetime_name, suggestions, optional_alternative } => {
diag.arg("lifetime_name", lifetime_name);
let msg = diag.eagerly_translate(
fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit,
@ -3382,8 +3400,8 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
diag.multipart_suggestion_with_style(
msg,
suggestions,
Applicability::MaybeIncorrect,
style(tool_only),
applicability(optional_alternative),
style(optional_alternative),
);
}
}

View file

@ -2156,6 +2156,7 @@ declare_lint! {
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
reference: "issue #52234 <https://github.com/rust-lang/rust/issues/52234>",
report_in_deps: true,
};
crate_level_only
}
@ -2839,7 +2840,7 @@ declare_lint! {
/// [issue #79813]: https://github.com/rust-lang/rust/issues/79813
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
Warn,
Deny,
"trailing semicolon in macro body used as expression",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
@ -2887,11 +2888,12 @@ declare_lint! {
/// struct S { /* fields */ }
/// ```
pub LEGACY_DERIVE_HELPERS,
Warn,
Deny,
"detects derive helper attributes that are used before they are introduced",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
reference: "issue #79202 <https://github.com/rust-lang/rust/issues/79202>",
report_in_deps: true,
};
}
@ -4624,11 +4626,12 @@ declare_lint! {
///
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub PRIVATE_MACRO_USE,
Warn,
Deny,
"detects certain macro bindings that should not be re-exported",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
reference: "issue #120192 <https://github.com/rust-lang/rust/issues/120192>",
report_in_deps: true,
};
}
@ -4828,7 +4831,7 @@ declare_lint! {
///
/// ### Example
///
/// ```rust
/// ```rust,compile_fail
/// #![doc = in_root!()]
///
/// macro_rules! in_root { () => { "" } }
@ -4853,11 +4856,12 @@ declare_lint! {
///
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub OUT_OF_SCOPE_MACRO_CALLS,
Warn,
Deny,
"detects out of scope calls to `macro_rules` in key-value attributes",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
reference: "issue #124535 <https://github.com/rust-lang/rust/issues/124535>",
report_in_deps: true,
};
}

View file

@ -171,6 +171,16 @@ fn main() {
let cxxflags = output(&mut cmd);
let mut cfg = cc::Build::new();
cfg.warnings(false);
// Prevent critical warnings when we're compiling from rust-lang/rust CI,
// except on MSVC, as the compiler throws warnings that are only reported
// for this platform. See https://github.com/rust-lang/rust/pull/145031#issuecomment-3162677202
// FIXME(llvm22): It looks like the specific problem code has been removed
// in https://github.com/llvm/llvm-project/commit/e8fc808bf8e78a3c80d1f8e293a92677b92366dd,
// retry msvc once we bump our LLVM version.
if std::env::var_os("CI").is_some() && !target.contains("msvc") {
cfg.warnings_into_errors(true);
}
for flag in cxxflags.split_whitespace() {
// Ignore flags like `-m64` when we're doing a cross build
if is_crossed && flag.starts_with("-m") {

View file

@ -37,28 +37,6 @@ static coverage::Counter fromRust(LLVMRustCounter Counter) {
report_fatal_error("Bad LLVMRustCounterKind!");
}
struct LLVMRustMCDCDecisionParameters {
uint32_t BitmapIdx;
uint16_t NumConditions;
};
struct LLVMRustMCDCBranchParameters {
int16_t ConditionID;
int16_t ConditionIDs[2];
};
static coverage::mcdc::BranchParameters
fromRust(LLVMRustMCDCBranchParameters Params) {
return coverage::mcdc::BranchParameters(
Params.ConditionID, {Params.ConditionIDs[0], Params.ConditionIDs[1]});
}
static coverage::mcdc::DecisionParameters
fromRust(LLVMRustMCDCDecisionParameters Params) {
return coverage::mcdc::DecisionParameters(Params.BitmapIdx,
Params.NumConditions);
}
// Must match the layout of
// `rustc_codegen_llvm::coverageinfo::ffi::CoverageSpan`.
struct LLVMRustCoverageSpan {
@ -90,22 +68,6 @@ struct LLVMRustCoverageBranchRegion {
LLVMRustCounter FalseCount;
};
// Must match the layout of
// `rustc_codegen_llvm::coverageinfo::ffi::MCDCBranchRegion`.
struct LLVMRustCoverageMCDCBranchRegion {
LLVMRustCoverageSpan Span;
LLVMRustCounter TrueCount;
LLVMRustCounter FalseCount;
LLVMRustMCDCBranchParameters MCDCBranchParams;
};
// Must match the layout of
// `rustc_codegen_llvm::coverageinfo::ffi::MCDCDecisionRegion`.
struct LLVMRustCoverageMCDCDecisionRegion {
LLVMRustCoverageSpan Span;
LLVMRustMCDCDecisionParameters MCDCDecisionParams;
};
// FFI equivalent of enum `llvm::coverage::CounterExpression::ExprKind`
// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L154
enum class LLVMRustCounterExprKind {
@ -159,10 +121,7 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer(
const LLVMRustCoverageExpansionRegion *ExpansionRegions,
size_t NumExpansionRegions,
const LLVMRustCoverageBranchRegion *BranchRegions, size_t NumBranchRegions,
const LLVMRustCoverageMCDCBranchRegion *MCDCBranchRegions,
size_t NumMCDCBranchRegions,
const LLVMRustCoverageMCDCDecisionRegion *MCDCDecisionRegions,
size_t NumMCDCDecisionRegions, RustStringRef BufferOut) {
RustStringRef BufferOut) {
// Convert from FFI representation to LLVM representation.
// Expressions:
@ -176,8 +135,8 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer(
}
std::vector<coverage::CounterMappingRegion> MappingRegions;
MappingRegions.reserve(NumCodeRegions + NumBranchRegions +
NumMCDCBranchRegions + NumMCDCDecisionRegions);
MappingRegions.reserve(NumCodeRegions + NumExpansionRegions +
NumBranchRegions);
// Code regions:
for (const auto &Region : ArrayRef(CodeRegions, NumCodeRegions)) {
@ -201,24 +160,6 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer(
Region.Span.LineEnd, Region.Span.ColumnEnd));
}
// MC/DC branch regions:
for (const auto &Region : ArrayRef(MCDCBranchRegions, NumMCDCBranchRegions)) {
MappingRegions.push_back(coverage::CounterMappingRegion::makeBranchRegion(
fromRust(Region.TrueCount), fromRust(Region.FalseCount),
Region.Span.FileID, Region.Span.LineStart, Region.Span.ColumnStart,
Region.Span.LineEnd, Region.Span.ColumnEnd,
fromRust(Region.MCDCBranchParams)));
}
// MC/DC decision regions:
for (const auto &Region :
ArrayRef(MCDCDecisionRegions, NumMCDCDecisionRegions)) {
MappingRegions.push_back(coverage::CounterMappingRegion::makeDecisionRegion(
fromRust(Region.MCDCDecisionParams), Region.Span.FileID,
Region.Span.LineStart, Region.Span.ColumnStart, Region.Span.LineEnd,
Region.Span.ColumnEnd));
}
// Write the converted expressions and mappings to a byte buffer.
auto CoverageMappingWriter = coverage::CoverageMappingWriter(
ArrayRef<unsigned>(VirtualFileMappingIDs, NumVirtualFileMappingIDs),

View file

@ -83,7 +83,7 @@ pub fn walk_native_lib_search_dirs<R>(
// Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks
// we must have the support library stubs in the library search path (#121430).
if let Some(sdk_root) = apple_sdk_root
&& sess.target.llvm_target.contains("macabi")
&& sess.target.env == "macabi"
{
f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?;
f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?;

View file

@ -19,13 +19,12 @@ use rustc_hir::find_attr;
use rustc_hir_pretty::id_to_string;
use rustc_middle::dep_graph::WorkProductId;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::metadata_symbol_name;
use rustc_middle::mir::interpret;
use rustc_middle::query::Providers;
use rustc_middle::traits::specialization_graph;
use rustc_middle::ty::AssocItemContainer;
use rustc_middle::ty::codec::TyEncoder;
use rustc_middle::ty::fast_reject::{self, TreatParams};
use rustc_middle::ty::{AssocItemContainer, SymbolName};
use rustc_middle::{bug, span_bug};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque};
use rustc_session::config::{CrateType, OptLevel, TargetModifier};
@ -2207,19 +2206,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportInfo)],
) -> LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)> {
empty_proc_macro!(self);
// The metadata symbol name is special. It should not show up in
// downstream crates.
let metadata_symbol_name = SymbolName::new(self.tcx, &metadata_symbol_name(self.tcx));
self.lazy_array(
exported_symbols
.iter()
.filter(|&(exported_symbol, _)| match *exported_symbol {
ExportedSymbol::NoDefId(symbol_name) => symbol_name != metadata_symbol_name,
_ => true,
})
.cloned(),
)
self.lazy_array(exported_symbols.iter().cloned())
}
fn encode_dylib_dependency_formats(&mut self) -> LazyArray<Option<LinkagePreference>> {

View file

@ -122,8 +122,6 @@ middle_strict_coherence_needs_negative_coherence =
to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled
.label = due to this attribute
middle_type_length_limit = reached the type-length limit while instantiating `{$shrunk}`
middle_type_length_limit = reached the type-length limit while instantiating `{$instance}`
middle_unsupported_union = we don't support unions yet: '{$ty_name}'
middle_written_to_path = the full type name has been written to '{$path}'

View file

@ -1,4 +1,4 @@
use std::path::{Path, PathBuf};
use std::path::Path;
use std::{fmt, io};
use rustc_errors::codes::*;
@ -6,7 +6,7 @@ use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
use crate::ty::Ty;
use crate::ty::{Instance, Ty};
#[derive(Diagnostic)]
#[diag(middle_drop_check_overflow, code = E0320)]
@ -161,13 +161,10 @@ pub(crate) struct ErroneousConstant {
#[derive(Diagnostic)]
#[diag(middle_type_length_limit)]
#[help(middle_consider_type_length_limit)]
pub(crate) struct TypeLengthLimit {
pub(crate) struct TypeLengthLimit<'tcx> {
#[primary_span]
pub span: Span,
pub shrunk: String,
#[note(middle_written_to_path)]
pub was_written: bool,
pub path: PathBuf,
pub instance: Instance<'tcx>,
pub type_length: usize,
}

View file

@ -50,25 +50,6 @@ rustc_index::newtype_index! {
pub struct ExpressionId {}
}
rustc_index::newtype_index! {
/// ID of a mcdc condition. Used by llvm to check mcdc coverage.
///
/// Note for future: the max limit of 0xFFFF is probably too loose. Actually llvm does not
/// support decisions with too many conditions (7 and more at LLVM 18 while may be hundreds at 19)
/// and represents it with `int16_t`. This max value may be changed once we could
/// figure out an accurate limit.
#[derive(HashStable)]
#[encodable]
#[orderable]
#[max = 0xFFFF]
#[debug_format = "ConditionId({})"]
pub struct ConditionId {}
}
impl ConditionId {
pub const START: Self = Self::from_usize(0);
}
/// Enum that can hold a constant zero value, the ID of an physical coverage
/// counter, or the ID of a coverage-counter expression.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
@ -109,16 +90,6 @@ pub enum CoverageKind {
/// During codegen, this might be lowered to `llvm.instrprof.increment` or
/// to a no-op, depending on the outcome of counter-creation.
VirtualCounter { bcb: BasicCoverageBlock },
/// Marks the point in MIR control flow represented by a evaluated condition.
///
/// This is eventually lowered to instruments updating mcdc temp variables.
CondBitmapUpdate { index: u32, decision_depth: u16 },
/// Marks the point in MIR control flow represented by a evaluated decision.
///
/// This is eventually lowered to `llvm.instrprof.mcdc.tvbitmap.update` in LLVM IR.
TestVectorBitmapUpdate { bitmap_idx: u32, decision_depth: u16 },
}
impl Debug for CoverageKind {
@ -128,12 +99,6 @@ impl Debug for CoverageKind {
SpanMarker => write!(fmt, "SpanMarker"),
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"),
CondBitmapUpdate { index, decision_depth } => {
write!(fmt, "CondBitmapUpdate(index={:?}, depth={:?})", index, decision_depth)
}
TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
write!(fmt, "TestVectorUpdate({:?}, depth={:?})", bitmap_idx, decision_depth)
}
}
}
}
@ -170,14 +135,6 @@ pub enum MappingKind {
Code { bcb: BasicCoverageBlock },
/// Associates a branch region with separate counters for true and false.
Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
/// Associates a branch region with separate counters for true and false.
MCDCBranch {
true_bcb: BasicCoverageBlock,
false_bcb: BasicCoverageBlock,
mcdc_params: ConditionInfo,
},
/// Associates a decision region with a bitmap and number of conditions.
MCDCDecision(DecisionInfo),
}
#[derive(Clone, Debug)]
@ -201,11 +158,6 @@ pub struct FunctionCoverageInfo {
pub priority_list: Vec<BasicCoverageBlock>,
pub mappings: Vec<Mapping>,
pub mcdc_bitmap_bits: usize,
/// The depth of the deepest decision is used to know how many
/// temp condbitmaps should be allocated for the function.
pub mcdc_num_condition_bitmaps: usize,
}
/// Coverage information for a function, recorded during MIR building and
@ -222,10 +174,6 @@ pub struct CoverageInfoHi {
/// data structures without having to scan the entire body first.
pub num_block_markers: usize,
pub branch_spans: Vec<BranchSpan>,
/// Branch spans generated by mcdc. Because of some limits mcdc builder give up generating
/// decisions including them so that they are handled as normal branch spans.
pub mcdc_degraded_branch_spans: Vec<MCDCBranchSpan>,
pub mcdc_spans: Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>,
}
#[derive(Clone, Debug)]
@ -236,39 +184,6 @@ pub struct BranchSpan {
pub false_marker: BlockMarkerId,
}
#[derive(Copy, Clone, Debug)]
#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
pub struct ConditionInfo {
pub condition_id: ConditionId,
pub true_next_id: Option<ConditionId>,
pub false_next_id: Option<ConditionId>,
}
#[derive(Clone, Debug)]
#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
pub struct MCDCBranchSpan {
pub span: Span,
pub condition_info: ConditionInfo,
pub true_marker: BlockMarkerId,
pub false_marker: BlockMarkerId,
}
#[derive(Copy, Clone, Debug)]
#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
pub struct DecisionInfo {
pub bitmap_idx: u32,
pub num_conditions: u16,
}
#[derive(Clone, Debug)]
#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
pub struct MCDCDecisionSpan {
pub span: Span,
pub end_markers: Vec<BlockMarkerId>,
pub decision_depth: u16,
pub num_conditions: usize,
}
/// Contains information needed during codegen, obtained by inspecting the
/// function's MIR after MIR optimizations.
///

View file

@ -585,12 +585,7 @@ fn write_coverage_info_hi(
coverage_info_hi: &coverage::CoverageInfoHi,
w: &mut dyn io::Write,
) -> io::Result<()> {
let coverage::CoverageInfoHi {
num_block_markers: _,
branch_spans,
mcdc_degraded_branch_spans,
mcdc_spans,
} = coverage_info_hi;
let coverage::CoverageInfoHi { num_block_markers: _, branch_spans } = coverage_info_hi;
// Only add an extra trailing newline if we printed at least one thing.
let mut did_print = false;
@ -603,38 +598,6 @@ fn write_coverage_info_hi(
did_print = true;
}
for coverage::MCDCBranchSpan { span, true_marker, false_marker, .. } in
mcdc_degraded_branch_spans
{
writeln!(
w,
"{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
)?;
did_print = true;
}
for (
coverage::MCDCDecisionSpan { span, end_markers, decision_depth, num_conditions: _ },
conditions,
) in mcdc_spans
{
let num_conditions = conditions.len();
writeln!(
w,
"{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
)?;
for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
conditions
{
writeln!(
w,
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
condition_info.condition_id
)?;
}
did_print = true;
}
if did_print {
writeln!(w)?;
}

View file

@ -1205,18 +1205,19 @@ macro_rules! visit_place_fns {
self.super_projection_elem(place_ref, elem, context, location);
}
fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
let mut context = context;
if !place.projection.is_empty() {
if context.is_use() {
// ^ Only change the context if it is a real use, not a "use" in debuginfo.
context = if context.is_mutating_use() {
PlaceContext::MutatingUse(MutatingUseContext::Projection)
} else {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
}
fn super_place(
&mut self,
place: &Place<'tcx>,
mut context: PlaceContext,
location: Location,
) {
if !place.projection.is_empty() && context.is_use() {
// ^ Only change the context if it is a real use, not a "use" in debuginfo.
context = if context.is_mutating_use() {
PlaceContext::MutatingUse(MutatingUseContext::Projection)
} else {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
}
self.visit_local(place.local, context, location);
@ -1239,7 +1240,7 @@ macro_rules! visit_place_fns {
&mut self,
_place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
_context: PlaceContext,
context: PlaceContext,
location: Location,
) {
match elem {
@ -1252,7 +1253,12 @@ macro_rules! visit_place_fns {
ProjectionElem::Index(local) => {
self.visit_local(
local,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
if context.is_use() {
// ^ Only change the context if it is a real use, not a "use" in debuginfo.
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy)
} else {
context
},
location,
);
}

View file

@ -1178,11 +1178,10 @@ rustc_queries! {
/// Return the live symbols in the crate for dead code check.
///
/// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone) and
/// their respective impl (i.e., part of the derive macro)
/// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone).
query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx (
LocalDefIdSet,
LocalDefIdMap<FxIndexSet<(DefId, DefId)>>
LocalDefIdMap<FxIndexSet<DefId>>,
) {
arena_cache
desc { "finding live symbols in crate" }

View file

@ -290,11 +290,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst);
let trait_def_id = self.parent(def_id);
debug_assert_matches!(self.def_kind(trait_def_id), DefKind::Trait);
let trait_generics = self.generics_of(trait_def_id);
(
ty::TraitRef::new_from_args(self, trait_def_id, args.truncate_to(self, trait_generics)),
&args[trait_generics.count()..],
)
let trait_ref = ty::TraitRef::from_assoc(self, trait_def_id, args);
(trait_ref, &args[trait_ref.args.len()..])
}
fn mk_args(self, args: &[Self::GenericArg]) -> ty::GenericArgsRef<'tcx> {

View file

@ -7,14 +7,14 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{
Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, listify, pluralize,
};
use rustc_hir::def::DefKind;
use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, AmbigArg, LangItem, PredicateOrigin, WherePredicateKind};
use rustc_span::{BytePos, Span};
use rustc_type_ir::TyKind::*;
use crate::ty::{
self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque,
self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Instance, Opaque,
PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
@ -28,6 +28,15 @@ impl IntoDiagArg for Ty<'_> {
}
}
impl IntoDiagArg for Instance<'_> {
fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
ty::tls::with(|tcx| {
let instance = tcx.short_string_namespace(self, path, Namespace::ValueNS);
rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(instance))
})
}
}
into_diag_arg_using_display! {
ty::Region<'_>,
}

View file

@ -160,7 +160,11 @@ impl<'tcx> Ty<'tcx> {
_ => {
let width = tcx.sess.diagnostic_width();
let length_limit = std::cmp::max(width / 4, 40);
format!("`{}`", tcx.string_with_limit(self, length_limit)).into()
format!(
"`{}`",
tcx.string_with_limit(self, length_limit, hir::def::Namespace::TypeNS)
)
.into()
}
}
}
@ -213,12 +217,12 @@ impl<'tcx> Ty<'tcx> {
}
impl<'tcx> TyCtxt<'tcx> {
pub fn string_with_limit<T>(self, t: T, length_limit: usize) -> String
pub fn string_with_limit<T>(self, t: T, length_limit: usize, ns: hir::def::Namespace) -> String
where
T: Copy + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>,
{
let mut type_limit = 50;
let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |p| {
let regular = FmtPrinter::print_string(self, ns, |p| {
self.lift(t).expect("could not lift for printing").print(p)
})
.expect("could not write to `String`");
@ -229,11 +233,7 @@ impl<'tcx> TyCtxt<'tcx> {
loop {
// Look for the longest properly trimmed path that still fits in length_limit.
short = with_forced_trimmed_paths!({
let mut p = FmtPrinter::new_with_limit(
self,
hir::def::Namespace::TypeNS,
rustc_session::Limit(type_limit),
);
let mut p = FmtPrinter::new_with_limit(self, ns, rustc_session::Limit(type_limit));
self.lift(t)
.expect("could not lift for printing")
.print(&mut p)
@ -251,12 +251,28 @@ impl<'tcx> TyCtxt<'tcx> {
/// When calling this after a `Diag` is constructed, the preferred way of doing so is
/// `tcx.short_string(ty, diag.long_ty_path())`. The diagnostic itself is the one that keeps
/// the existence of a "long type" anywhere in the diagnostic, so the note telling the user
/// where we wrote the file to is only printed once.
/// where we wrote the file to is only printed once. The path will use the type namespace.
pub fn short_string<T>(self, t: T, path: &mut Option<PathBuf>) -> String
where
T: Copy + Hash + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>,
{
let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |p| {
self.short_string_namespace(t, path, hir::def::Namespace::TypeNS)
}
/// When calling this after a `Diag` is constructed, the preferred way of doing so is
/// `tcx.short_string(ty, diag.long_ty_path())`. The diagnostic itself is the one that keeps
/// the existence of a "long type" anywhere in the diagnostic, so the note telling the user
/// where we wrote the file to is only printed once.
pub fn short_string_namespace<T>(
self,
t: T,
path: &mut Option<PathBuf>,
namespace: hir::def::Namespace,
) -> String
where
T: Copy + Hash + for<'a, 'b> Lift<TyCtxt<'b>, Lifted: Print<'b, FmtPrinter<'a, 'b>>>,
{
let regular = FmtPrinter::print_string(self, namespace, |p| {
self.lift(t).expect("could not lift for printing").print(p)
})
.expect("could not write to `String`");
@ -270,7 +286,7 @@ impl<'tcx> TyCtxt<'tcx> {
if regular.len() <= width * 2 / 3 {
return regular;
}
let short = self.string_with_limit(t, length_limit);
let short = self.string_with_limit(t, length_limit, namespace);
if regular == short {
return regular;
}

View file

@ -527,21 +527,28 @@ impl<'tcx> GenericArgs<'tcx> {
#[inline]
#[track_caller]
pub fn type_at(&self, i: usize) -> Ty<'tcx> {
self[i].as_type().unwrap_or_else(|| bug!("expected type for param #{} in {:?}", i, self))
self[i].as_type().unwrap_or_else(
#[track_caller]
|| bug!("expected type for param #{} in {:?}", i, self),
)
}
#[inline]
#[track_caller]
pub fn region_at(&self, i: usize) -> ty::Region<'tcx> {
self[i]
.as_region()
.unwrap_or_else(|| bug!("expected region for param #{} in {:?}", i, self))
self[i].as_region().unwrap_or_else(
#[track_caller]
|| bug!("expected region for param #{} in {:?}", i, self),
)
}
#[inline]
#[track_caller]
pub fn const_at(&self, i: usize) -> ty::Const<'tcx> {
self[i].as_const().unwrap_or_else(|| bug!("expected const for param #{} in {:?}", i, self))
self[i].as_const().unwrap_or_else(
#[track_caller]
|| bug!("expected const for param #{} in {:?}", i, self),
)
}
#[inline]
@ -578,6 +585,9 @@ impl<'tcx> GenericArgs<'tcx> {
tcx.mk_args_from_iter(target_args.iter().chain(self.iter().skip(defs.count())))
}
/// Truncates this list of generic args to have at most the number of args in `generics`.
///
/// You might be looking for [`TraitRef::from_assoc`](super::TraitRef::from_assoc).
pub fn truncate_to(&self, tcx: TyCtxt<'tcx>, generics: &ty::Generics) -> GenericArgsRef<'tcx> {
tcx.mk_args(&self[..generics.count()])
}

View file

@ -1,6 +1,5 @@
use std::assert_matches::assert_matches;
use std::fmt;
use std::path::PathBuf;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorGuaranteed;
@ -17,7 +16,7 @@ use tracing::{debug, instrument};
use crate::error;
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::ty::normalize_erasing_regions::NormalizationError;
use crate::ty::print::{FmtPrinter, Printer, shrunk_instance_name};
use crate::ty::print::{FmtPrinter, Print};
use crate::ty::{
self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor,
@ -389,59 +388,15 @@ fn type_length<'tcx>(item: impl TypeVisitable<TyCtxt<'tcx>>) -> usize {
visitor.type_length
}
pub fn fmt_instance(
f: &mut fmt::Formatter<'_>,
instance: Instance<'_>,
type_length: Option<rustc_session::Limit>,
) -> fmt::Result {
ty::tls::with(|tcx| {
let args = tcx.lift(instance.args).expect("could not lift for printing");
let mut p = if let Some(type_length) = type_length {
FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length)
} else {
FmtPrinter::new(tcx, Namespace::ValueNS)
};
p.print_def_path(instance.def_id(), args)?;
let s = p.into_buffer();
f.write_str(&s)
})?;
match instance.def {
InstanceKind::Item(_) => Ok(()),
InstanceKind::VTableShim(_) => write!(f, " - shim(vtable)"),
InstanceKind::ReifyShim(_, None) => write!(f, " - shim(reify)"),
InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr)) => write!(f, " - shim(reify-fnptr)"),
InstanceKind::ReifyShim(_, Some(ReifyReason::Vtable)) => write!(f, " - shim(reify-vtable)"),
InstanceKind::ThreadLocalShim(_) => write!(f, " - shim(tls)"),
InstanceKind::Intrinsic(_) => write!(f, " - intrinsic"),
InstanceKind::Virtual(_, num) => write!(f, " - virtual#{num}"),
InstanceKind::FnPtrShim(_, ty) => write!(f, " - shim({ty})"),
InstanceKind::ClosureOnceShim { .. } => write!(f, " - shim"),
InstanceKind::ConstructCoroutineInClosureShim { .. } => write!(f, " - shim"),
InstanceKind::DropGlue(_, None) => write!(f, " - shim(None)"),
InstanceKind::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
InstanceKind::CloneShim(_, ty) => write!(f, " - shim({ty})"),
InstanceKind::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"),
InstanceKind::FutureDropPollShim(_, proxy_ty, impl_ty) => {
write!(f, " - dropshim({proxy_ty}-{impl_ty})")
}
InstanceKind::AsyncDropGlue(_, ty) => write!(f, " - shim({ty})"),
InstanceKind::AsyncDropGlueCtorShim(_, ty) => write!(f, " - shim(Some({ty}))"),
}
}
pub struct ShortInstance<'tcx>(pub Instance<'tcx>, pub usize);
impl<'tcx> fmt::Display for ShortInstance<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_instance(f, self.0, Some(rustc_session::Limit(self.1)))
}
}
impl<'tcx> fmt::Display for Instance<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_instance(f, *self, None)
ty::tls::with(|tcx| {
let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
let instance = tcx.lift(*self).expect("could not lift for printing");
instance.print(&mut p)?;
let s = p.into_buffer();
f.write_str(&s)
})
}
}
@ -610,23 +565,12 @@ impl<'tcx> Instance<'tcx> {
Ok(None) => {
let type_length = type_length(args);
if !tcx.type_length_limit().value_within_limit(type_length) {
let (shrunk, written_to_path) =
shrunk_instance_name(tcx, Instance::new_raw(def_id, args));
let mut path = PathBuf::new();
let was_written = if let Some(path2) = written_to_path {
path = path2;
true
} else {
false
};
tcx.dcx().emit_fatal(error::TypeLengthLimit {
// We don't use `def_span(def_id)` so that diagnostics point
// to the crate root during mono instead of to foreign items.
// This is arguably better.
span: span_or_local_def_span(),
shrunk,
was_written,
path,
instance: Instance::new_raw(def_id, args),
type_length,
});
} else {

View file

@ -82,7 +82,7 @@ pub use self::context::{
TyCtxtFeed, tls,
};
pub use self::fold::*;
pub use self::instance::{Instance, InstanceKind, ReifyReason, ShortInstance, UnusedGenericParams};
pub use self::instance::{Instance, InstanceKind, ReifyReason, UnusedGenericParams};
pub use self::list::{List, ListWithCachedTypeInfo};
pub use self::opaque_types::OpaqueTypeKey;
pub use self::pattern::{Pattern, PatternKind};

View file

@ -6,8 +6,7 @@ use rustc_macros::{HashStable, extension};
use rustc_type_ir as ir;
use crate::ty::{
self, DebruijnIndex, EarlyBinder, PredicatePolarity, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom,
WithCachedTypeInfo,
self, DebruijnIndex, EarlyBinder, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom, WithCachedTypeInfo,
};
pub type TraitRef<'tcx> = ir::TraitRef<TyCtxt<'tcx>>;
@ -536,15 +535,6 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, TraitRef<'tcx>>> for Clause
}
}
impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, TraitRef<'tcx>>> for PolyTraitPredicate<'tcx> {
fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, _tcx: TyCtxt<'tcx>) -> Self {
from.map_bound(|trait_ref| TraitPredicate {
trait_ref,
polarity: PredicatePolarity::Positive,
})
}
}
impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TraitPredicate<'tcx>> for Predicate<'tcx> {
fn upcast_from(from: TraitPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
PredicateKind::Clause(ClauseKind::Trait(from)).upcast(tcx)

View file

@ -1,5 +1,3 @@
use std::path::PathBuf;
use hir::def::Namespace;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sso::SsoHashSet;
@ -8,7 +6,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
use tracing::{debug, instrument, trace};
use crate::ty::{self, GenericArg, ShortInstance, Ty, TyCtxt};
use crate::ty::{self, GenericArg, Ty, TyCtxt};
// `pretty` is a separate module only for organization.
mod pretty;
@ -317,6 +315,43 @@ impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> {
}
}
impl<'tcx, P: Printer<'tcx> + std::fmt::Write> Print<'tcx, P> for ty::Instance<'tcx> {
fn print(&self, cx: &mut P) -> Result<(), PrintError> {
cx.print_def_path(self.def_id(), self.args)?;
match self.def {
ty::InstanceKind::Item(_) => {}
ty::InstanceKind::VTableShim(_) => cx.write_str(" - shim(vtable)")?,
ty::InstanceKind::ReifyShim(_, None) => cx.write_str(" - shim(reify)")?,
ty::InstanceKind::ReifyShim(_, Some(ty::ReifyReason::FnPtr)) => {
cx.write_str(" - shim(reify-fnptr)")?
}
ty::InstanceKind::ReifyShim(_, Some(ty::ReifyReason::Vtable)) => {
cx.write_str(" - shim(reify-vtable)")?
}
ty::InstanceKind::ThreadLocalShim(_) => cx.write_str(" - shim(tls)")?,
ty::InstanceKind::Intrinsic(_) => cx.write_str(" - intrinsic")?,
ty::InstanceKind::Virtual(_, num) => cx.write_str(&format!(" - virtual#{num}"))?,
ty::InstanceKind::FnPtrShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?,
ty::InstanceKind::ClosureOnceShim { .. } => cx.write_str(" - shim")?,
ty::InstanceKind::ConstructCoroutineInClosureShim { .. } => cx.write_str(" - shim")?,
ty::InstanceKind::DropGlue(_, None) => cx.write_str(" - shim(None)")?,
ty::InstanceKind::DropGlue(_, Some(ty)) => {
cx.write_str(&format!(" - shim(Some({ty}))"))?
}
ty::InstanceKind::CloneShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?,
ty::InstanceKind::FnPtrAddrShim(_, ty) => cx.write_str(&format!(" - shim({ty})"))?,
ty::InstanceKind::FutureDropPollShim(_, proxy_ty, impl_ty) => {
cx.write_str(&format!(" - dropshim({proxy_ty}-{impl_ty})"))?
}
ty::InstanceKind::AsyncDropGlue(_, ty) => cx.write_str(&format!(" - shim({ty})"))?,
ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
cx.write_str(&format!(" - shim(Some({ty}))"))?
}
};
Ok(())
}
}
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
fn print(&self, p: &mut P) -> Result<(), PrintError> {
p.print_dyn_existential(self)
@ -356,31 +391,3 @@ where
with_no_trimmed_paths!(Self::print(t, fmt))
}
}
/// Format instance name that is already known to be too long for rustc.
/// Show only the first 2 types if it is longer than 32 characters to avoid blasting
/// the user's terminal with thousands of lines of type-name.
///
/// If the type name is longer than before+after, it will be written to a file.
pub fn shrunk_instance_name<'tcx>(
tcx: TyCtxt<'tcx>,
instance: ty::Instance<'tcx>,
) -> (String, Option<PathBuf>) {
let s = instance.to_string();
// Only use the shrunk version if it's really shorter.
// This also avoids the case where before and after slices overlap.
if s.chars().nth(33).is_some() {
let shrunk = format!("{}", ShortInstance(instance, 4));
if shrunk == s {
return (s, None);
}
let path = tcx.output_filenames(()).temp_path_for_diagnostic("long-type.txt");
let written_to_path = std::fs::write(&path, s).ok().map(|_| path);
(shrunk, written_to_path)
} else {
(s, None)
}
}

View file

@ -2987,7 +2987,7 @@ impl<'tcx> ty::Binder<'tcx, ty::TraitRef<'tcx>> {
}
}
#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)]
#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift, Hash)]
pub struct TraitPredPrintModifiersAndPath<'tcx>(ty::TraitPredicate<'tcx>);
impl<'tcx> fmt::Debug for TraitPredPrintModifiersAndPath<'tcx> {

View file

@ -121,8 +121,6 @@ mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
.note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
.label = dereference of raw pointer
mir_build_exceeds_mcdc_condition_limit = number of conditions in decision ({$num_conditions}) exceeds limit ({$max_conditions}), so MC/DC analysis will not count this expression
mir_build_extern_static_requires_unsafe =
use of extern static is unsafe and requires unsafe block
.note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior

View file

@ -6,7 +6,7 @@ use rustc_span::Span;
use tracing::debug;
use crate::builder::ForGuard::OutsideGuard;
use crate::builder::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops};
use crate::builder::matches::{DeclareLetBindings, ScheduleDrops};
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
impl<'a, 'tcx> Builder<'a, 'tcx> {
@ -199,15 +199,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None,
Some((Some(&destination), initializer_span)),
);
this.visit_primary_bindings(pattern, &mut |this, node, span| {
this.storage_live_binding(
block,
node,
span,
OutsideGuard,
ScheduleDrops::Yes,
);
});
let else_block_span = this.thir[*else_block].span;
let (matching, failure) =
this.in_if_then_scope(last_remainder_scope, else_block_span, |this| {
@ -218,7 +209,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None,
initializer_span,
DeclareLetBindings::No,
EmitStorageLive::No,
)
});
matching.and(failure)

View file

@ -8,11 +8,8 @@ use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir};
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::LocalDefId;
use crate::builder::coverageinfo::mcdc::MCDCInfoBuilder;
use crate::builder::{Builder, CFG};
mod mcdc;
/// Collects coverage-related information during MIR building, to eventually be
/// turned into a function's [`CoverageInfoHi`] when MIR building is complete.
pub(crate) struct CoverageInfoBuilder {
@ -23,8 +20,6 @@ pub(crate) struct CoverageInfoBuilder {
/// Present if branch coverage is enabled.
branch_info: Option<BranchInfo>,
/// Present if MC/DC coverage is enabled.
mcdc_info: Option<MCDCInfoBuilder>,
}
#[derive(Default)]
@ -83,7 +78,6 @@ impl CoverageInfoBuilder {
nots: FxHashMap::default(),
markers: BlockMarkerGen::default(),
branch_info: tcx.sess.instrument_coverage_branch().then(BranchInfo::default),
mcdc_info: tcx.sess.instrument_coverage_mcdc().then(MCDCInfoBuilder::new),
})
}
@ -135,26 +129,11 @@ impl CoverageInfoBuilder {
fn register_two_way_branch<'tcx>(
&mut self,
tcx: TyCtxt<'tcx>,
cfg: &mut CFG<'tcx>,
source_info: SourceInfo,
true_block: BasicBlock,
false_block: BasicBlock,
) {
// Separate path for handling branches when MC/DC is enabled.
if let Some(mcdc_info) = self.mcdc_info.as_mut() {
let inject_block_marker =
|source_info, block| self.markers.inject_block_marker(cfg, source_info, block);
mcdc_info.visit_evaluated_condition(
tcx,
source_info,
true_block,
false_block,
inject_block_marker,
);
return;
}
// Bail out if branch coverage is not enabled.
let Some(branch_info) = self.branch_info.as_mut() else { return };
@ -169,23 +148,14 @@ impl CoverageInfoBuilder {
}
pub(crate) fn into_done(self) -> Box<CoverageInfoHi> {
let Self { nots: _, markers: BlockMarkerGen { num_block_markers }, branch_info, mcdc_info } =
self;
let Self { nots: _, markers: BlockMarkerGen { num_block_markers }, branch_info } = self;
let branch_spans =
branch_info.map(|branch_info| branch_info.branch_spans).unwrap_or_default();
let (mcdc_spans, mcdc_degraded_branch_spans) =
mcdc_info.map(MCDCInfoBuilder::into_done).unwrap_or_default();
// For simplicity, always return an info struct (without Option), even
// if there's nothing interesting in it.
Box::new(CoverageInfoHi {
num_block_markers,
branch_spans,
mcdc_degraded_branch_spans,
mcdc_spans,
})
Box::new(CoverageInfoHi { num_block_markers, branch_spans })
}
}
@ -238,14 +208,7 @@ impl<'tcx> Builder<'_, 'tcx> {
mir::TerminatorKind::if_(mir::Operand::Copy(place), true_block, false_block),
);
// Separate path for handling branches when MC/DC is enabled.
coverage_info.register_two_way_branch(
self.tcx,
&mut self.cfg,
source_info,
true_block,
false_block,
);
coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
let join_block = self.cfg.start_new_block();
self.cfg.goto(true_block, source_info, join_block);
@ -276,13 +239,7 @@ impl<'tcx> Builder<'_, 'tcx> {
let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };
coverage_info.register_two_way_branch(
self.tcx,
&mut self.cfg,
source_info,
then_block,
else_block,
);
coverage_info.register_two_way_branch(&mut self.cfg, source_info, then_block, else_block);
}
/// If branch coverage is enabled, inject marker statements into `true_block`
@ -299,12 +256,6 @@ impl<'tcx> Builder<'_, 'tcx> {
let Some(coverage_info) = self.coverage_info.as_mut() else { return };
let source_info = SourceInfo { span: pattern.span, scope: self.source_scope };
coverage_info.register_two_way_branch(
self.tcx,
&mut self.cfg,
source_info,
true_block,
false_block,
);
coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
}
}

View file

@ -1,295 +0,0 @@
use std::collections::VecDeque;
use rustc_middle::bug;
use rustc_middle::mir::coverage::{
BlockMarkerId, ConditionId, ConditionInfo, MCDCBranchSpan, MCDCDecisionSpan,
};
use rustc_middle::mir::{BasicBlock, SourceInfo};
use rustc_middle::thir::LogicalOp;
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
use crate::builder::Builder;
use crate::errors::MCDCExceedsConditionLimit;
/// LLVM uses `i16` to represent condition id. Hence `i16::MAX` is the hard limit for number of
/// conditions in a decision.
const MAX_CONDITIONS_IN_DECISION: usize = i16::MAX as usize;
#[derive(Default)]
struct MCDCDecisionCtx {
/// To construct condition evaluation tree.
decision_stack: VecDeque<ConditionInfo>,
processing_decision: Option<MCDCDecisionSpan>,
conditions: Vec<MCDCBranchSpan>,
}
struct MCDCState {
decision_ctx_stack: Vec<MCDCDecisionCtx>,
}
impl MCDCState {
fn new() -> Self {
Self { decision_ctx_stack: vec![MCDCDecisionCtx::default()] }
}
/// Decision depth is given as a u16 to reduce the size of the `CoverageKind`,
/// as it is very unlikely that the depth ever reaches 2^16.
#[inline]
fn decision_depth(&self) -> u16 {
match u16::try_from(self.decision_ctx_stack.len())
.expect(
"decision depth did not fit in u16, this is likely to be an instrumentation error",
)
.checked_sub(1)
{
Some(d) => d,
None => bug!("Unexpected empty decision stack"),
}
}
// At first we assign ConditionIds for each sub expression.
// If the sub expression is composite, re-assign its ConditionId to its LHS and generate a new ConditionId for its RHS.
//
// Example: "x = (A && B) || (C && D) || (D && F)"
//
// Visit Depth1:
// (A && B) || (C && D) || (D && F)
// ^-------LHS--------^ ^-RHS--^
// ID=1 ID=2
//
// Visit LHS-Depth2:
// (A && B) || (C && D)
// ^-LHS--^ ^-RHS--^
// ID=1 ID=3
//
// Visit LHS-Depth3:
// (A && B)
// LHS RHS
// ID=1 ID=4
//
// Visit RHS-Depth3:
// (C && D)
// LHS RHS
// ID=3 ID=5
//
// Visit RHS-Depth2: (D && F)
// LHS RHS
// ID=2 ID=6
//
// Visit Depth1:
// (A && B) || (C && D) || (D && F)
// ID=1 ID=4 ID=3 ID=5 ID=2 ID=6
//
// A node ID of '0' always means MC/DC isn't being tracked.
//
// If a "next" node ID is '0', it means it's the end of the test vector.
//
// As the compiler tracks expression in pre-order, we can ensure that condition info of parents are always properly assigned when their children are visited.
// - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
// - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
fn record_conditions(&mut self, op: LogicalOp, span: Span) {
let decision_depth = self.decision_depth();
let Some(decision_ctx) = self.decision_ctx_stack.last_mut() else {
bug!("Unexpected empty decision_ctx_stack")
};
let decision = match decision_ctx.processing_decision.as_mut() {
Some(decision) => {
decision.span = decision.span.to(span);
decision
}
None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
span,
num_conditions: 0,
end_markers: vec![],
decision_depth,
}),
};
let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_else(|| {
assert_eq!(
decision.num_conditions, 0,
"decision stack must be empty only for empty decision"
);
decision.num_conditions += 1;
ConditionInfo {
condition_id: ConditionId::START,
true_next_id: None,
false_next_id: None,
}
});
let lhs_id = parent_condition.condition_id;
let rhs_condition_id = ConditionId::from(decision.num_conditions);
decision.num_conditions += 1;
let (lhs, rhs) = match op {
LogicalOp::And => {
let lhs = ConditionInfo {
condition_id: lhs_id,
true_next_id: Some(rhs_condition_id),
false_next_id: parent_condition.false_next_id,
};
let rhs = ConditionInfo {
condition_id: rhs_condition_id,
true_next_id: parent_condition.true_next_id,
false_next_id: parent_condition.false_next_id,
};
(lhs, rhs)
}
LogicalOp::Or => {
let lhs = ConditionInfo {
condition_id: lhs_id,
true_next_id: parent_condition.true_next_id,
false_next_id: Some(rhs_condition_id),
};
let rhs = ConditionInfo {
condition_id: rhs_condition_id,
true_next_id: parent_condition.true_next_id,
false_next_id: parent_condition.false_next_id,
};
(lhs, rhs)
}
};
// We visit expressions tree in pre-order, so place the left-hand side on the top.
decision_ctx.decision_stack.push_back(rhs);
decision_ctx.decision_stack.push_back(lhs);
}
fn try_finish_decision(
&mut self,
span: Span,
true_marker: BlockMarkerId,
false_marker: BlockMarkerId,
degraded_branches: &mut Vec<MCDCBranchSpan>,
) -> Option<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)> {
let Some(decision_ctx) = self.decision_ctx_stack.last_mut() else {
bug!("Unexpected empty decision_ctx_stack")
};
let Some(condition_info) = decision_ctx.decision_stack.pop_back() else {
let branch = MCDCBranchSpan {
span,
condition_info: ConditionInfo {
condition_id: ConditionId::START,
true_next_id: None,
false_next_id: None,
},
true_marker,
false_marker,
};
degraded_branches.push(branch);
return None;
};
let Some(decision) = decision_ctx.processing_decision.as_mut() else {
bug!("Processing decision should have been created before any conditions are taken");
};
if condition_info.true_next_id.is_none() {
decision.end_markers.push(true_marker);
}
if condition_info.false_next_id.is_none() {
decision.end_markers.push(false_marker);
}
decision_ctx.conditions.push(MCDCBranchSpan {
span,
condition_info,
true_marker,
false_marker,
});
if decision_ctx.decision_stack.is_empty() {
let conditions = std::mem::take(&mut decision_ctx.conditions);
decision_ctx.processing_decision.take().map(|decision| (decision, conditions))
} else {
None
}
}
}
pub(crate) struct MCDCInfoBuilder {
degraded_spans: Vec<MCDCBranchSpan>,
mcdc_spans: Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>,
state: MCDCState,
}
impl MCDCInfoBuilder {
pub(crate) fn new() -> Self {
Self { degraded_spans: vec![], mcdc_spans: vec![], state: MCDCState::new() }
}
pub(crate) fn visit_evaluated_condition(
&mut self,
tcx: TyCtxt<'_>,
source_info: SourceInfo,
true_block: BasicBlock,
false_block: BasicBlock,
mut inject_block_marker: impl FnMut(SourceInfo, BasicBlock) -> BlockMarkerId,
) {
let true_marker = inject_block_marker(source_info, true_block);
let false_marker = inject_block_marker(source_info, false_block);
// take_condition() returns Some for decision_result when the decision stack
// is empty, i.e. when all the conditions of the decision were instrumented,
// and the decision is "complete".
if let Some((decision, conditions)) = self.state.try_finish_decision(
source_info.span,
true_marker,
false_marker,
&mut self.degraded_spans,
) {
let num_conditions = conditions.len();
assert_eq!(
num_conditions, decision.num_conditions,
"final number of conditions is not correct"
);
match num_conditions {
0 => {
unreachable!("Decision with no condition is not expected");
}
1..=MAX_CONDITIONS_IN_DECISION => {
self.mcdc_spans.push((decision, conditions));
}
_ => {
self.degraded_spans.extend(conditions);
tcx.dcx().emit_warn(MCDCExceedsConditionLimit {
span: decision.span,
num_conditions,
max_conditions: MAX_CONDITIONS_IN_DECISION,
});
}
}
}
}
pub(crate) fn into_done(
self,
) -> (Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>, Vec<MCDCBranchSpan>) {
(self.mcdc_spans, self.degraded_spans)
}
}
impl Builder<'_, '_> {
pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) {
if let Some(coverage_info) = self.coverage_info.as_mut()
&& let Some(mcdc_info) = coverage_info.mcdc_info.as_mut()
{
mcdc_info.state.record_conditions(logical_op, span);
}
}
pub(crate) fn mcdc_increment_depth_if_enabled(&mut self) {
if let Some(coverage_info) = self.coverage_info.as_mut()
&& let Some(mcdc_info) = coverage_info.mcdc_info.as_mut()
{
mcdc_info.state.decision_ctx_stack.push(MCDCDecisionCtx::default());
};
}
pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) {
if let Some(coverage_info) = self.coverage_info.as_mut()
&& let Some(mcdc_info) = coverage_info.mcdc_info.as_mut()
&& mcdc_info.state.decision_ctx_stack.pop().is_none()
{
bug!("Unexpected empty decision stack");
};
}
}

View file

@ -159,8 +159,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let condition_scope = this.local_scope();
let source_info = this.source_info(expr.span);
this.visit_coverage_branch_operation(op, expr.span);
// We first evaluate the left-hand side of the predicate ...
let (then_block, else_block) =
this.in_if_then_scope(condition_scope, expr.span, |this| {

View file

@ -124,9 +124,19 @@ impl<'tcx> MatchPairTree<'tcx> {
let test_case = match pattern.kind {
PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
PatKind::Or { ref pats } => Some(TestCase::Or {
pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(),
}),
PatKind::Or { ref pats } => {
let pats: Box<[FlatPat<'tcx>]> =
pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
if !pats[0].extra_data.bindings.is_empty() {
// Hold a place for any bindings established in (possibly-nested) or-patterns.
// By only holding a place when bindings are present, we skip over any
// or-patterns that will be simplified by `merge_trivial_subcandidates`. In
// other words, we can assume this expands into subcandidates.
// FIXME(@dianne): this needs updating/removing if we always merge or-patterns
extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
}
Some(TestCase::Or { pats })
}
PatKind::Range(ref range) => {
if range.is_full_range(cx.tcx) == Some(true) {
@ -194,12 +204,12 @@ impl<'tcx> MatchPairTree<'tcx> {
// Then push this binding, after any bindings in the subpattern.
if let Some(source) = place {
extra_data.bindings.push(super::Binding {
extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
span: pattern.span,
source,
var_id: var,
binding_mode: mode,
});
}));
}
None

View file

@ -5,11 +5,11 @@
//! This also includes code for pattern bindings in `let` statements and
//! function parameters.
use std::assert_matches::assert_matches;
use std::borrow::Borrow;
use std::mem;
use std::sync::Arc;
use itertools::{Itertools, Position};
use rustc_abi::VariantIdx;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
@ -69,18 +69,6 @@ pub(crate) enum DeclareLetBindings {
LetNotPermitted,
}
/// Used by [`Builder::bind_matched_candidate_for_arm_body`] to determine
/// whether or not to call [`Builder::storage_live_binding`] to emit
/// [`StatementKind::StorageLive`].
#[derive(Clone, Copy)]
pub(crate) enum EmitStorageLive {
/// Yes, emit `StorageLive` as normal.
Yes,
/// No, don't emit `StorageLive`. The caller has taken responsibility for
/// emitting `StorageLive` as appropriate.
No,
}
/// Used by [`Builder::storage_live_binding`] and [`Builder::bind_matched_candidate_for_arm_body`]
/// to decide whether to schedule drops.
#[derive(Clone, Copy, Debug)]
@ -125,15 +113,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let expr_span = expr.span;
match expr.kind {
ExprKind::LogicalOp { op: op @ LogicalOp::And, lhs, rhs } => {
this.visit_coverage_branch_operation(op, expr_span);
ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
let lhs_then_block = this.then_else_break_inner(block, lhs, args).into_block();
let rhs_then_block =
this.then_else_break_inner(lhs_then_block, rhs, args).into_block();
rhs_then_block.unit()
}
ExprKind::LogicalOp { op: op @ LogicalOp::Or, lhs, rhs } => {
this.visit_coverage_branch_operation(op, expr_span);
ExprKind::LogicalOp { op: LogicalOp::Or, lhs, rhs } => {
let local_scope = this.local_scope();
let (lhs_success_block, failure_block) =
this.in_if_then_scope(local_scope, expr_span, |this| {
@ -207,16 +193,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Some(args.variable_source_info.scope),
args.variable_source_info.span,
args.declare_let_bindings,
EmitStorageLive::Yes,
),
_ => {
let mut block = block;
let temp_scope = args.temp_scope_override.unwrap_or_else(|| this.local_scope());
let mutability = Mutability::Mut;
// Increment the decision depth, in case we encounter boolean expressions
// further down.
this.mcdc_increment_depth_if_enabled();
let place = unpack!(
block = this.as_temp(
block,
@ -228,7 +210,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
mutability
)
);
this.mcdc_decrement_depth_if_enabled();
let operand = Operand::Move(Place::from(place));
@ -479,7 +460,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&built_match_tree.fake_borrow_temps,
scrutinee_span,
Some((arm, match_scope)),
EmitStorageLive::Yes,
);
this.fixed_temps_scope = old_dedup_scope;
@ -533,7 +513,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
scrutinee_span: Span,
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
emit_storage_live: EmitStorageLive,
) -> BasicBlock {
if branch.sub_branches.len() == 1 {
let [sub_branch] = branch.sub_branches.try_into().unwrap();
@ -544,7 +523,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
scrutinee_span,
arm_match_scope,
ScheduleDrops::Yes,
emit_storage_live,
)
} else {
// It's helpful to avoid scheduling drops multiple times to save
@ -561,27 +539,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// return: it isn't bound by move until right before enter the arm.
// To handle this we instead unschedule it's drop after each time
// we lower the guard.
// As a result, we end up with the drop order of the last sub-branch we lower. To use
// the drop order for the first sub-branch, we lower sub-branches in reverse (#142163).
let target_block = self.cfg.start_new_block();
let mut schedule_drops = ScheduleDrops::Yes;
let arm = arm_match_scope.unzip().0;
// We keep a stack of all of the bindings and type ascriptions
// from the parent candidates that we visit, that also need to
// be bound for each candidate.
for sub_branch in branch.sub_branches {
if let Some(arm) = arm {
self.clear_top_scope(arm.scope);
}
for (pos, sub_branch) in branch.sub_branches.into_iter().rev().with_position() {
debug_assert!(pos != Position::Only);
let schedule_drops =
if pos == Position::Last { ScheduleDrops::Yes } else { ScheduleDrops::No };
let binding_end = self.bind_and_guard_matched_candidate(
sub_branch,
fake_borrow_temps,
scrutinee_span,
arm_match_scope,
schedule_drops,
emit_storage_live,
);
if arm.is_none() {
schedule_drops = ScheduleDrops::No;
}
self.cfg.goto(binding_end, outer_source_info, target_block);
}
@ -741,7 +712,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&[],
irrefutable_pat.span,
None,
EmitStorageLive::Yes,
)
.unit()
}
@ -996,7 +966,7 @@ struct PatternExtraData<'tcx> {
span: Span,
/// Bindings that must be established.
bindings: Vec<Binding<'tcx>>,
bindings: Vec<SubpatternBindings<'tcx>>,
/// Types that must be asserted.
ascriptions: Vec<Ascription<'tcx>>,
@ -1011,6 +981,15 @@ impl<'tcx> PatternExtraData<'tcx> {
}
}
#[derive(Debug, Clone)]
enum SubpatternBindings<'tcx> {
/// A single binding.
One(Binding<'tcx>),
/// Holds the place for an or-pattern's bindings. This ensures their drops are scheduled in the
/// order the primary bindings appear. See rust-lang/rust#142163 for more information.
FromOrPattern,
}
/// A pattern in a form suitable for lowering the match tree, with all irrefutable
/// patterns simplified away.
///
@ -1226,7 +1205,7 @@ fn traverse_candidate<'tcx, C, T, I>(
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Copy, Debug)]
struct Binding<'tcx> {
span: Span,
source: Place<'tcx>,
@ -1452,12 +1431,7 @@ impl<'tcx> MatchTreeSubBranch<'tcx> {
span: candidate.extra_data.span,
success_block: candidate.pre_binding_block.unwrap(),
otherwise_block: candidate.otherwise_block.unwrap(),
bindings: parent_data
.iter()
.flat_map(|d| &d.bindings)
.chain(&candidate.extra_data.bindings)
.cloned()
.collect(),
bindings: sub_branch_bindings(parent_data, &candidate.extra_data.bindings),
ascriptions: parent_data
.iter()
.flat_map(|d| &d.ascriptions)
@ -1490,6 +1464,68 @@ impl<'tcx> MatchTreeBranch<'tcx> {
}
}
/// Collects the bindings for a [`MatchTreeSubBranch`], preserving the order they appear in the
/// pattern, as though the or-alternatives chosen in this sub-branch were inlined.
fn sub_branch_bindings<'tcx>(
parents: &[PatternExtraData<'tcx>],
leaf_bindings: &[SubpatternBindings<'tcx>],
) -> Vec<Binding<'tcx>> {
// In the common case, all bindings will be in leaves. Allocate to fit the leaf's bindings.
let mut all_bindings = Vec::with_capacity(leaf_bindings.len());
let mut remainder = parents
.iter()
.map(|parent| parent.bindings.as_slice())
.chain([leaf_bindings])
// Skip over unsimplified or-patterns without bindings.
.filter(|bindings| !bindings.is_empty());
if let Some(candidate_bindings) = remainder.next() {
push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder);
}
// Make sure we've included all bindings. For ill-formed patterns like `(x, _ | y)`, we may not
// have collected all bindings yet, since we only check the first alternative when determining
// whether to inline subcandidates' bindings.
// FIXME(@dianne): prevent ill-formed patterns from getting here
while let Some(candidate_bindings) = remainder.next() {
ty::tls::with(|tcx| {
tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted")
});
// To recover, we collect the rest in an arbitrary order.
push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder);
}
all_bindings
}
/// Helper for [`sub_branch_bindings`]. Collects bindings from `candidate_bindings` into
/// `flattened`. Bindings in or-patterns are collected recursively from `remainder`.
fn push_sub_branch_bindings<'c, 'tcx: 'c>(
flattened: &mut Vec<Binding<'tcx>>,
candidate_bindings: &'c [SubpatternBindings<'tcx>],
remainder: &mut impl Iterator<Item = &'c [SubpatternBindings<'tcx>]>,
) {
for subpat_bindings in candidate_bindings {
match subpat_bindings {
SubpatternBindings::One(binding) => flattened.push(*binding),
SubpatternBindings::FromOrPattern => {
// Inline bindings from an or-pattern. By construction, this always
// corresponds to a subcandidate and its closest descendants (i.e. those
// from nested or-patterns, but not adjacent or-patterns). To handle
// adjacent or-patterns, e.g. `(x | x, y | y)`, we update the `remainder` to
// point to the first descendant candidate from outside this or-pattern.
if let Some(subcandidate_bindings) = remainder.next() {
push_sub_branch_bindings(flattened, subcandidate_bindings, remainder);
} else {
// For ill-formed patterns like `x | _`, we may not have any subcandidates left
// to inline bindings from.
// FIXME(@dianne): prevent ill-formed patterns from getting here
ty::tls::with(|tcx| {
tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted")
});
};
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum HasMatchGuard {
Yes,
@ -2364,7 +2400,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_scope: Option<SourceScope>,
scope_span: Span,
declare_let_bindings: DeclareLetBindings,
emit_storage_live: EmitStorageLive,
) -> BlockAnd<()> {
let expr_span = self.thir[expr_id].span;
let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
@ -2398,14 +2433,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
let success = self.bind_pattern(
self.source_info(pat.span),
branch,
&[],
expr_span,
None,
emit_storage_live,
);
let success = self.bind_pattern(self.source_info(pat.span), branch, &[], expr_span, None);
// If branch coverage is enabled, record this branch.
self.visit_coverage_conditional_let(pat, success, built_tree.otherwise_block);
@ -2428,7 +2456,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
scrutinee_span: Span,
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
schedule_drops: ScheduleDrops,
emit_storage_live: EmitStorageLive,
) -> BasicBlock {
debug!("bind_and_guard_matched_candidate(subbranch={:?})", sub_branch);
@ -2453,11 +2480,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Bindings for guards require some extra handling to automatically
// insert implicit references/dereferences.
self.bind_matched_candidate_for_guard(
block,
schedule_drops,
sub_branch.bindings.iter(),
);
// This always schedules storage drops, so we may need to unschedule them below.
self.bind_matched_candidate_for_guard(block, sub_branch.bindings.iter());
let guard_frame = GuardFrame {
locals: sub_branch
.bindings
@ -2489,6 +2513,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
)
});
// If this isn't the final sub-branch being lowered, we need to unschedule drops of
// bindings and temporaries created for and by the guard. As a result, the drop order
// for the arm will correspond to the binding order of the final sub-branch lowered.
if matches!(schedule_drops, ScheduleDrops::No) {
self.clear_top_scope(arm.scope);
}
let source_info = self.source_info(guard_span);
let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span));
let guard_frame = self.guard_context.pop().unwrap();
@ -2538,16 +2569,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let cause = FakeReadCause::ForGuardBinding;
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
}
assert_matches!(
schedule_drops,
ScheduleDrops::Yes,
"patterns with guards must schedule drops"
);
// Only schedule drops for the last sub-branch we lower.
self.bind_matched_candidate_for_arm_body(
post_guard_block,
ScheduleDrops::Yes,
schedule_drops,
by_value_bindings,
emit_storage_live,
);
post_guard_block
@ -2559,7 +2585,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block,
schedule_drops,
sub_branch.bindings.iter(),
emit_storage_live,
);
block
}
@ -2671,7 +2696,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn bind_matched_candidate_for_guard<'b>(
&mut self,
block: BasicBlock,
schedule_drops: ScheduleDrops,
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
) where
'tcx: 'b,
@ -2690,12 +2714,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// a reference R: &T pointing to the location matched by
// the pattern, and every occurrence of P within a guard
// denotes *R.
// Drops must be scheduled to emit `StorageDead` on the guard's failure/break branches.
let ref_for_guard = self.storage_live_binding(
block,
binding.var_id,
binding.span,
RefWithinGuard,
schedule_drops,
ScheduleDrops::Yes,
);
match binding.binding_mode.0 {
ByRef::No => {
@ -2705,13 +2730,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
}
ByRef::Yes(mutbl) => {
// The arm binding will be by reference, so eagerly create it now.
// The arm binding will be by reference, so eagerly create it now. Drops must
// be scheduled to emit `StorageDead` on the guard's failure/break branches.
let value_for_arm = self.storage_live_binding(
block,
binding.var_id,
binding.span,
OutsideGuard,
schedule_drops,
ScheduleDrops::Yes,
);
let rvalue =
@ -2730,7 +2756,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block: BasicBlock,
schedule_drops: ScheduleDrops,
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
emit_storage_live: EmitStorageLive,
) where
'tcx: 'b,
{
@ -2740,19 +2765,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Assign each of the bindings. This may trigger moves out of the candidate.
for binding in bindings {
let source_info = self.source_info(binding.span);
let local = match emit_storage_live {
// Here storages are already alive, probably because this is a binding
// from let-else.
// We just need to schedule drop for the value.
EmitStorageLive::No => self.var_local_id(binding.var_id, OutsideGuard).into(),
EmitStorageLive::Yes => self.storage_live_binding(
block,
binding.var_id,
binding.span,
OutsideGuard,
schedule_drops,
),
};
let local = self.storage_live_binding(
block,
binding.var_id,
binding.span,
OutsideGuard,
schedule_drops,
);
if matches!(schedule_drops, ScheduleDrops::Yes) {
self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
}

View file

@ -138,7 +138,9 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) {
for binding in &candidate.extra_data.bindings {
self.visit_binding(binding);
if let super::SubpatternBindings::One(binding) = binding {
self.visit_binding(binding);
}
}
for match_pair in &candidate.match_pairs {
self.visit_match_pair(match_pair);
@ -147,7 +149,9 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'tcx>) {
for binding in &flat_pat.extra_data.bindings {
self.visit_binding(binding);
if let super::SubpatternBindings::One(binding) = binding {
self.visit_binding(binding);
}
}
for match_pair in &flat_pat.match_pairs {
self.visit_match_pair(match_pair);

View file

@ -60,9 +60,13 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
let BodyTy::Fn(caller_sig) = self.thir.body_type else {
span_bug!(
call.span,
"`become` outside of functions should have been disallowed by hit_typeck"
"`become` outside of functions should have been disallowed by hir_typeck"
)
};
// While the `caller_sig` does have its regions erased, it does not have its
// binders anonymized. We call `erase_regions` once again to anonymize any binders
// within the signature, such as in function pointer or `dyn Trait` args.
let caller_sig = self.tcx.erase_regions(caller_sig);
let ExprKind::Scope { value, .. } = call.kind else {
span_bug!(call.span, "expected scope, found: {call:?}")

View file

@ -975,15 +975,6 @@ pub(crate) struct NonEmptyNeverPattern<'tcx> {
pub(crate) ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(mir_build_exceeds_mcdc_condition_limit)]
pub(crate) struct MCDCExceedsConditionLimit {
#[primary_span]
pub(crate) span: Span,
pub(crate) num_conditions: usize,
pub(crate) max_conditions: usize,
}
#[derive(Diagnostic)]
#[diag(mir_build_pattern_not_covered, code = E0005)]
pub(crate) struct PatternNotCovered<'s, 'tcx> {

View file

@ -9,8 +9,6 @@ mir_transform_const_mut_borrow = taking a mutable reference to a `const` item
.note2 = the mutable reference will refer to this temporary, not the original `const` item
.note3 = mutable reference created due to call to this method
mir_transform_exceeds_mcdc_test_vector_limit = number of total test vectors in one function will exceed limit ({$max_num_test_vectors}) if this decision is instrumented, so MC/DC analysis ignores it
mir_transform_ffi_unwind_call = call to {$foreign ->
[true] foreign function
*[false] function pointer

View file

@ -45,12 +45,6 @@ pub(super) fn is_inline_valid_on_fn<'tcx>(
return Err("#[rustc_no_mir_inline]");
}
// FIXME(#127234): Coverage instrumentation currently doesn't handle inlined
// MIR correctly when Modified Condition/Decision Coverage is enabled.
if tcx.sess.instrument_coverage_mcdc() {
return Err("incompatible with MC/DC coverage");
}
let ty = tcx.type_of(def_id);
if match ty.instantiate_identity().kind() {
ty::FnDef(..) => tcx.fn_sig(def_id).instantiate_identity().c_variadic(),

View file

@ -1,10 +1,5 @@
use std::collections::BTreeSet;
use rustc_data_structures::fx::FxIndexMap;
use rustc_index::IndexVec;
use rustc_middle::mir::coverage::{
BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageInfoHi, CoverageKind,
};
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind};
use rustc_middle::mir::{self, BasicBlock, StatementKind};
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
@ -13,7 +8,6 @@ use crate::coverage::ExtractedHirInfo;
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
use crate::coverage::spans::extract_refined_covspans;
use crate::coverage::unexpand::unexpand_into_body_span;
use crate::errors::MCDCExceedsTestVectorLimit;
/// Associates an ordinary executable code span with its corresponding BCB.
#[derive(Debug)]
@ -22,9 +16,6 @@ pub(super) struct CodeMapping {
pub(super) bcb: BasicCoverageBlock,
}
/// This is separate from [`MCDCBranch`] to help prepare for larger changes
/// that will be needed for improved branch coverage in the future.
/// (See <https://github.com/rust-lang/rust/pull/124217>.)
#[derive(Debug)]
pub(super) struct BranchPair {
pub(super) span: Span,
@ -32,40 +23,10 @@ pub(super) struct BranchPair {
pub(super) false_bcb: BasicCoverageBlock,
}
/// Associates an MC/DC branch span with condition info besides fields for normal branch.
#[derive(Debug)]
pub(super) struct MCDCBranch {
pub(super) span: Span,
pub(super) true_bcb: BasicCoverageBlock,
pub(super) false_bcb: BasicCoverageBlock,
pub(super) condition_info: ConditionInfo,
// Offset added to test vector idx if this branch is evaluated to true.
pub(super) true_index: usize,
// Offset added to test vector idx if this branch is evaluated to false.
pub(super) false_index: usize,
}
/// Associates an MC/DC decision with its join BCBs.
#[derive(Debug)]
pub(super) struct MCDCDecision {
pub(super) span: Span,
pub(super) end_bcbs: BTreeSet<BasicCoverageBlock>,
pub(super) bitmap_idx: usize,
pub(super) num_test_vectors: usize,
pub(super) decision_depth: u16,
}
// LLVM uses `i32` to index the bitmap. Thus `i32::MAX` is the hard limit for number of all test vectors
// in a function.
const MCDC_MAX_BITMAP_SIZE: usize = i32::MAX as usize;
#[derive(Default)]
pub(super) struct ExtractedMappings {
pub(super) code_mappings: Vec<CodeMapping>,
pub(super) branch_pairs: Vec<BranchPair>,
pub(super) mcdc_bitmap_bits: usize,
pub(super) mcdc_degraded_branches: Vec<MCDCBranch>,
pub(super) mcdc_mappings: Vec<(MCDCDecision, Vec<MCDCBranch>)>,
}
/// Extracts coverage-relevant spans from MIR, and associates them with
@ -78,32 +39,13 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
) -> ExtractedMappings {
let mut code_mappings = vec![];
let mut branch_pairs = vec![];
let mut mcdc_bitmap_bits = 0;
let mut mcdc_degraded_branches = vec![];
let mut mcdc_mappings = vec![];
// Extract ordinary code mappings from MIR statement/terminator spans.
extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings);
branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph));
extract_mcdc_mappings(
mir_body,
tcx,
hir_info.body_span,
graph,
&mut mcdc_bitmap_bits,
&mut mcdc_degraded_branches,
&mut mcdc_mappings,
);
ExtractedMappings {
code_mappings,
branch_pairs,
mcdc_bitmap_bits,
mcdc_degraded_branches,
mcdc_mappings,
}
ExtractedMappings { code_mappings, branch_pairs }
}
fn resolve_block_markers(
@ -127,12 +69,6 @@ fn resolve_block_markers(
block_markers
}
// FIXME: There is currently a lot of redundancy between
// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so
// that they can each be modified without interfering with the other, but in
// the long term we should try to bring them together again when branch coverage
// and MC/DC coverage support are more mature.
pub(super) fn extract_branch_pairs(
mir_body: &mir::Body<'_>,
hir_info: &ExtractedHirInfo,
@ -162,175 +98,3 @@ pub(super) fn extract_branch_pairs(
})
.collect::<Vec<_>>()
}
pub(super) fn extract_mcdc_mappings(
mir_body: &mir::Body<'_>,
tcx: TyCtxt<'_>,
body_span: Span,
graph: &CoverageGraph,
mcdc_bitmap_bits: &mut usize,
mcdc_degraded_branches: &mut impl Extend<MCDCBranch>,
mcdc_mappings: &mut impl Extend<(MCDCDecision, Vec<MCDCBranch>)>,
) {
let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return };
let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
let bcb_from_marker = |marker: BlockMarkerId| graph.bcb_from_bb(block_markers[marker]?);
let check_branch_bcb =
|raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| {
// For now, ignore any branch span that was introduced by
// expansion. This makes things like assert macros less noisy.
if !raw_span.ctxt().outer_expn_data().is_root() {
return None;
}
let span = unexpand_into_body_span(raw_span, body_span)?;
let true_bcb = bcb_from_marker(true_marker)?;
let false_bcb = bcb_from_marker(false_marker)?;
Some((span, true_bcb, false_bcb))
};
let to_mcdc_branch = |&mir::coverage::MCDCBranchSpan {
span: raw_span,
condition_info,
true_marker,
false_marker,
}| {
let (span, true_bcb, false_bcb) = check_branch_bcb(raw_span, true_marker, false_marker)?;
Some(MCDCBranch {
span,
true_bcb,
false_bcb,
condition_info,
true_index: usize::MAX,
false_index: usize::MAX,
})
};
let mut get_bitmap_idx = |num_test_vectors: usize| -> Option<usize> {
let bitmap_idx = *mcdc_bitmap_bits;
let next_bitmap_bits = bitmap_idx.saturating_add(num_test_vectors);
(next_bitmap_bits <= MCDC_MAX_BITMAP_SIZE).then(|| {
*mcdc_bitmap_bits = next_bitmap_bits;
bitmap_idx
})
};
mcdc_degraded_branches
.extend(coverage_info_hi.mcdc_degraded_branch_spans.iter().filter_map(to_mcdc_branch));
mcdc_mappings.extend(coverage_info_hi.mcdc_spans.iter().filter_map(|(decision, branches)| {
if branches.len() == 0 {
return None;
}
let decision_span = unexpand_into_body_span(decision.span, body_span)?;
let end_bcbs = decision
.end_markers
.iter()
.map(|&marker| bcb_from_marker(marker))
.collect::<Option<_>>()?;
let mut branch_mappings: Vec<_> = branches.into_iter().filter_map(to_mcdc_branch).collect();
if branch_mappings.len() != branches.len() {
mcdc_degraded_branches.extend(branch_mappings);
return None;
}
let num_test_vectors = calc_test_vectors_index(&mut branch_mappings);
let Some(bitmap_idx) = get_bitmap_idx(num_test_vectors) else {
tcx.dcx().emit_warn(MCDCExceedsTestVectorLimit {
span: decision_span,
max_num_test_vectors: MCDC_MAX_BITMAP_SIZE,
});
mcdc_degraded_branches.extend(branch_mappings);
return None;
};
// LLVM requires span of the decision contains all spans of its conditions.
// Usually the decision span meets the requirement well but in cases like macros it may not.
let span = branch_mappings
.iter()
.map(|branch| branch.span)
.reduce(|lhs, rhs| lhs.to(rhs))
.map(
|joint_span| {
if decision_span.contains(joint_span) { decision_span } else { joint_span }
},
)
.expect("branch mappings are ensured to be non-empty as checked above");
Some((
MCDCDecision {
span,
end_bcbs,
bitmap_idx,
num_test_vectors,
decision_depth: decision.decision_depth,
},
branch_mappings,
))
}));
}
// LLVM checks the executed test vector by accumulating indices of tested branches.
// We calculate number of all possible test vectors of the decision and assign indices
// to branches here.
// See [the rfc](https://discourse.llvm.org/t/rfc-coverage-new-algorithm-and-file-format-for-mc-dc/76798/)
// for more details about the algorithm.
// This function is mostly like [`TVIdxBuilder::TvIdxBuilder`](https://github.com/llvm/llvm-project/blob/d594d9f7f4dc6eb748b3261917db689fdc348b96/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp#L226)
fn calc_test_vectors_index(conditions: &mut Vec<MCDCBranch>) -> usize {
let mut indegree_stats = IndexVec::<ConditionId, usize>::from_elem_n(0, conditions.len());
// `num_paths` is `width` described at the llvm rfc, which indicates how many paths reaching the condition node.
let mut num_paths_stats = IndexVec::<ConditionId, usize>::from_elem_n(0, conditions.len());
let mut next_conditions = conditions
.iter_mut()
.map(|branch| {
let ConditionInfo { condition_id, true_next_id, false_next_id } = branch.condition_info;
[true_next_id, false_next_id]
.into_iter()
.flatten()
.for_each(|next_id| indegree_stats[next_id] += 1);
(condition_id, branch)
})
.collect::<FxIndexMap<_, _>>();
let mut queue =
std::collections::VecDeque::from_iter(next_conditions.swap_remove(&ConditionId::START));
num_paths_stats[ConditionId::START] = 1;
let mut decision_end_nodes = Vec::new();
while let Some(branch) = queue.pop_front() {
let ConditionInfo { condition_id, true_next_id, false_next_id } = branch.condition_info;
let (false_index, true_index) = (&mut branch.false_index, &mut branch.true_index);
let this_paths_count = num_paths_stats[condition_id];
// Note. First check the false next to ensure conditions are touched in same order with llvm-cov.
for (next, index) in [(false_next_id, false_index), (true_next_id, true_index)] {
if let Some(next_id) = next {
let next_paths_count = &mut num_paths_stats[next_id];
*index = *next_paths_count;
*next_paths_count = next_paths_count.saturating_add(this_paths_count);
let next_indegree = &mut indegree_stats[next_id];
*next_indegree -= 1;
if *next_indegree == 0 {
queue.push_back(next_conditions.swap_remove(&next_id).expect(
"conditions with non-zero indegree before must be in next_conditions",
));
}
} else {
decision_end_nodes.push((this_paths_count, condition_id, index));
}
}
}
assert!(next_conditions.is_empty(), "the decision tree has untouched nodes");
let mut cur_idx = 0;
// LLVM hopes the end nodes are sorted in descending order by `num_paths` so that it can
// optimize bitmap size for decisions in tree form such as `a && b && c && d && ...`.
decision_end_nodes.sort_by_key(|(num_paths, _, _)| usize::MAX - *num_paths);
for (num_paths, condition_id, index) in decision_end_nodes {
assert_eq!(
num_paths, num_paths_stats[condition_id],
"end nodes should not be updated since they were visited"
);
assert_eq!(*index, usize::MAX, "end nodes should not be assigned index before");
*index = cur_idx;
cur_idx += num_paths;
}
cur_idx
}

View file

@ -10,9 +10,7 @@ mod unexpand;
use rustc_hir as hir;
use rustc_hir::intravisit::{Visitor, walk_expr};
use rustc_middle::hir::nested_filter;
use rustc_middle::mir::coverage::{
CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind,
};
use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo, Mapping, MappingKind};
use rustc_middle::mir::{self, BasicBlock, Statement, StatementKind, TerminatorKind};
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
@ -95,14 +93,6 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
// Inject coverage statements into MIR.
inject_coverage_statements(mir_body, &graph);
inject_mcdc_statements(mir_body, &graph, &extracted_mappings);
let mcdc_num_condition_bitmaps = extracted_mappings
.mcdc_mappings
.iter()
.map(|&(mappings::MCDCDecision { decision_depth, .. }, _)| decision_depth)
.max()
.map_or(0, |max| usize::from(max) + 1);
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
function_source_hash: hir_info.function_source_hash,
@ -111,9 +101,6 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
priority_list,
mappings,
mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits,
mcdc_num_condition_bitmaps,
}));
}
@ -124,13 +111,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
/// function can potentially be simplified even further.
fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec<Mapping> {
// Fully destructure the mappings struct to make sure we don't miss any kinds.
let ExtractedMappings {
code_mappings,
branch_pairs,
mcdc_bitmap_bits: _,
mcdc_degraded_branches,
mcdc_mappings,
} = extracted_mappings;
let ExtractedMappings { code_mappings, branch_pairs } = extracted_mappings;
let mut mappings = Vec::new();
mappings.extend(code_mappings.iter().map(
@ -148,57 +129,6 @@ fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec<Mapping> {
},
));
// MCDC branch mappings are appended with their decisions in case decisions were ignored.
mappings.extend(mcdc_degraded_branches.iter().map(
|&mappings::MCDCBranch {
span,
true_bcb,
false_bcb,
condition_info: _,
true_index: _,
false_index: _,
}| { Mapping { kind: MappingKind::Branch { true_bcb, false_bcb }, span } },
));
for (decision, branches) in mcdc_mappings {
// FIXME(#134497): Previously it was possible for some of these branch
// conversions to fail, in which case the remaining branches in the
// decision would be degraded to plain `MappingKind::Branch`.
// The changes in #134497 made that failure impossible, because the
// fallible step was deferred to codegen. But the corresponding code
// in codegen wasn't updated to detect the need for a degrade step.
let conditions = branches
.into_iter()
.map(
|&mappings::MCDCBranch {
span,
true_bcb,
false_bcb,
condition_info,
true_index: _,
false_index: _,
}| {
Mapping {
kind: MappingKind::MCDCBranch {
true_bcb,
false_bcb,
mcdc_params: condition_info,
},
span,
}
},
)
.collect::<Vec<_>>();
// LLVM requires end index for counter mapping regions.
let kind = MappingKind::MCDCDecision(DecisionInfo {
bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32,
num_conditions: u16::try_from(conditions.len()).unwrap(),
});
let span = decision.span;
mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter()));
}
mappings
}
@ -210,51 +140,6 @@ fn inject_coverage_statements<'tcx>(mir_body: &mut mir::Body<'tcx>, graph: &Cove
}
}
/// For each conditions inject statements to update condition bitmap after it has been evaluated.
/// For each decision inject statements to update test vector bitmap after it has been evaluated.
fn inject_mcdc_statements<'tcx>(
mir_body: &mut mir::Body<'tcx>,
graph: &CoverageGraph,
extracted_mappings: &ExtractedMappings,
) {
for (decision, conditions) in &extracted_mappings.mcdc_mappings {
// Inject test vector update first because `inject_statement` always insert new statement at head.
for &end in &decision.end_bcbs {
let end_bb = graph[end].leader_bb();
inject_statement(
mir_body,
CoverageKind::TestVectorBitmapUpdate {
bitmap_idx: decision.bitmap_idx as u32,
decision_depth: decision.decision_depth,
},
end_bb,
);
}
for &mappings::MCDCBranch {
span: _,
true_bcb,
false_bcb,
condition_info: _,
true_index,
false_index,
} in conditions
{
for (index, bcb) in [(false_index, false_bcb), (true_index, true_bcb)] {
let bb = graph[bcb].leader_bb();
inject_statement(
mir_body,
CoverageKind::CondBitmapUpdate {
index: index as u32,
decision_depth: decision.decision_depth,
},
bb,
);
}
}
}
}
fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb: BasicBlock) {
debug!(" injecting statement {counter_kind:?} for {bb:?}");
let data = &mut mir_body[bb];

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