commit
5a11197efd
631 changed files with 37491 additions and 8026 deletions
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
|
|
@ -7,6 +7,6 @@ tracking issue or there are none, feel free to ignore this.
|
|||
This PR will get automatically assigned to a reviewer. In case you would like
|
||||
a specific user to review your work, you can assign it to them by using
|
||||
|
||||
r\? <reviewer name> (with the `\` removed)
|
||||
r? <reviewer name>
|
||||
-->
|
||||
<!-- homu-ignore:end -->
|
||||
|
|
|
|||
|
|
@ -185,9 +185,6 @@ name = "anyhow"
|
|||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ar_archive_writer"
|
||||
|
|
@ -3499,6 +3496,7 @@ name = "rustc_codegen_llvm"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"gimli 0.30.0",
|
||||
"itertools",
|
||||
"libc",
|
||||
"measureme",
|
||||
|
|
@ -4117,7 +4115,6 @@ name = "rustc_middle"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"derive-where",
|
||||
"either",
|
||||
"field-offset",
|
||||
"gsgdt",
|
||||
|
|
|
|||
116
RELEASES.md
116
RELEASES.md
|
|
@ -1,3 +1,119 @@
|
|||
Version 1.84.0 (2025-01-09)
|
||||
==========================
|
||||
|
||||
<a id="
|
||||
Language"></a>
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Allow `#[deny]` inside `#[forbid]` as a no-op](https://github.com/rust-lang/rust/pull/121560/)
|
||||
- [Show a warning when `-Ctarget-feature` is used to toggle features that can lead to unsoundness due to ABI mismatches](https://github.com/rust-lang/rust/pull/129884)
|
||||
- [Use the next-generation trait solver in coherence](https://github.com/rust-lang/rust/pull/130654)
|
||||
- [Allow coercions to drop the principal of trait objects](https://github.com/rust-lang/rust/pull/131857)
|
||||
- [Support `/` as the path separator for `include!()` in all cases on Windows](https://github.com/rust-lang/rust/pull/125205)
|
||||
- [Taking a raw ref (`raw (const|mut)`) of a deref of a pointer (`*ptr`) is now safe](https://github.com/rust-lang/rust/pull/129248)
|
||||
- [Stabilize s390x inline assembly](https://github.com/rust-lang/rust/pull/131258)
|
||||
- [Stabilize Arm64EC inline assembly](https://github.com/rust-lang/rust/pull/131781)
|
||||
- [Lint against creating pointers to immediately dropped temporaries](https://github.com/rust-lang/rust/pull/128985)
|
||||
- [Execute drop glue when unwinding in an `extern "C"` function](https://github.com/rust-lang/rust/pull/129582)
|
||||
|
||||
<a id="1.84.0-Compiler"></a>
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [Add `--print host-tuple` flag to print the host target tuple and affirm the "target tuple" terminology over "target triple"](https://github.com/rust-lang/rust/pull/125579)
|
||||
- [Declaring functions with a calling convention not supported on the current target now triggers a hard error](https://github.com/rust-lang/rust/pull/129935)
|
||||
- [Set up indirect access to external data for `loongarch64-unknown-linux-{musl,ohos}`](https://github.com/rust-lang/rust/pull/131583)
|
||||
- [Enable XRay instrumentation for LoongArch Linux targets](https://github.com/rust-lang/rust/pull/131818)
|
||||
- [Extend the `unexpected_cfgs` lint to also warn in external macros](https://github.com/rust-lang/rust/pull/132577)
|
||||
- [Stabilize WebAssembly `multivalue`, `reference-types`, and `tail-call` target features](https://github.com/rust-lang/rust/pull/131080)
|
||||
- [Added Tier 2 support for the `wasm32v1-none` target](https://github.com/rust-lang/rust/pull/131487)
|
||||
|
||||
<a id="1.84.0-Libraries"></a>
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [Implement `From<&mut {slice}>` for `Box/Rc/Arc<{slice}>`](https://github.com/rust-lang/rust/pull/129329)
|
||||
- [Move `<float>::copysign`, `<float>::abs`, `<float>::signum` to `core`](https://github.com/rust-lang/rust/pull/131304)
|
||||
- [Add `LowerExp` and `UpperExp` implementations to `NonZero`](https://github.com/rust-lang/rust/pull/131377)
|
||||
- [Implement `FromStr` for `CString` and `TryFrom<CString>` for `String`](https://github.com/rust-lang/rust/pull/130608)
|
||||
- [`std::os::darwin` has been made public](https://github.com/rust-lang/rust/pull/130635)
|
||||
|
||||
<a id="1.84.0-Stabilized-APIs"></a>
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
- [`Ipv6Addr::is_unique_local`](https://doc.rust-lang.org/stable/core/net/struct.Ipv6Addr.html#method.is_unique_local)
|
||||
- [`Ipv6Addr::is_unicast_link_local`](https://doc.rust-lang.org/stable/core/net/struct.Ipv6Addr.html#method.is_unicast_link_local)
|
||||
- [`core::ptr::with_exposed_provenance`](https://doc.rust-lang.org/stable/core/ptr/fn.with_exposed_provenance.html)
|
||||
- [`core::ptr::with_exposed_provenance_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.with_exposed_provenance_mut.html)
|
||||
- [`<ptr>::addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.addr)
|
||||
- [`<ptr>::expose_provenance`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.expose_provenance)
|
||||
- [`<ptr>::with_addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.with_addr)
|
||||
- [`<ptr>::map_addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.map_addr)
|
||||
- [`<int>::isqrt`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.isqrt)
|
||||
- [`<int>::checked_isqrt`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.checked_isqrt)
|
||||
- [`<uint>::isqrt`](https://doc.rust-lang.org/stable/core/primitive.u32.html#method.isqrt)
|
||||
- [`NonZero::isqrt`](https://doc.rust-lang.org/stable/core/num/struct.NonZero.html#impl-NonZero%3Cu128%3E/method.isqrt)
|
||||
- [`core::ptr::without_provenance`](https://doc.rust-lang.org/stable/core/ptr/fn.without_provenance.html)
|
||||
- [`core::ptr::without_provenance_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.without_provenance_mut.html)
|
||||
- [`core::ptr::dangling`](https://doc.rust-lang.org/stable/core/ptr/fn.dangling.html)
|
||||
- [`core::ptr::dangling_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.dangling_mut.html)
|
||||
|
||||
These APIs are now stable in const contexts
|
||||
|
||||
- [`AtomicBool::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicBool.html#method.from_ptr)
|
||||
- [`AtomicPtr::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicPtr.html#method.from_ptr)
|
||||
- [`AtomicU8::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU8.html#method.from_ptr)
|
||||
- [`AtomicU16::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU16.html#method.from_ptr)
|
||||
- [`AtomicU32::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU32.html#method.from_ptr)
|
||||
- [`AtomicU64::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU64.html#method.from_ptr)
|
||||
- [`AtomicUsize::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicUsize.html#method.from_ptr)
|
||||
- [`AtomicI8::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI8.html#method.from_ptr)
|
||||
- [`AtomicI16::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI16.html#method.from_ptr)
|
||||
- [`AtomicI32::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI32.html#method.from_ptr)
|
||||
- [`AtomicI64::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI64.html#method.from_ptr)
|
||||
- [`AtomicIsize::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicIsize.html#method.from_ptr)
|
||||
- [`<ptr>::is_null`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_null-1)
|
||||
- [`<ptr>::as_ref`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.as_ref-1)
|
||||
- [`<ptr>::as_mut`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.as_mut)
|
||||
- [`Pin::new`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.new)
|
||||
- [`Pin::new_unchecked`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.new_unchecked)
|
||||
- [`Pin::get_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_ref)
|
||||
- [`Pin::into_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.into_ref)
|
||||
- [`Pin::get_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_mut)
|
||||
- [`Pin::get_unchecked_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_unchecked_mut)
|
||||
- [`Pin::static_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.static_ref)
|
||||
- [`Pin::static_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.static_mut)
|
||||
|
||||
<a id="1.84.0-Cargo"></a>
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [Stabilize MSRV-aware resolver config](https://github.com/rust-lang/cargo/pull/14639/)
|
||||
- [Stabilize resolver v3](https://github.com/rust-lang/cargo/pull/14754/)
|
||||
|
||||
<a id="1.84-Rustdoc"></a>
|
||||
|
||||
Rustdoc
|
||||
-------
|
||||
|
||||
- [rustdoc-search: improve type-driven search](https://github.com/rust-lang/rust/pull/127589)
|
||||
|
||||
<a id="1.84.0-Compatibility-Notes"></a>
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Enable by default the `LSX` target feature for LoongArch Linux targets](https://github.com/rust-lang/rust/pull/132140)
|
||||
- [The unstable `-Zprofile` flag (“gcov-style” coverage instrumentation) has been removed.](https://github.com/rust-lang/rust/pull/131829) This does not affect the stable flags for coverage instrumentation (`-Cinstrument-coverage`) and profile-guided optimization (`-Cprofile-generate`, `-Cprofile-use`), which are unrelated and remain available.
|
||||
- Support for the target named `wasm32-wasi` has been removed as the target is now named `wasm32-wasip1`. This completes the [transition](https://github.com/rust-lang/compiler-team/issues/607) [plan](https://github.com/rust-lang/compiler-team/issues/695) for this target following [the introduction of `wasm32-wasip1`](https://github.com/rust-lang/rust/pull/120468) in Rust 1.78. Compiler warnings on [use of `wasm32-wasi`](https://github.com/rust-lang/rust/pull/126662) introduced in Rust 1.81 are now gone as well as the target is removed.
|
||||
- [The syntax `&pin (mut|const) T` is now parsed as a type which in theory could affect macro expansion results in some edge cases](https://github.com/rust-lang/rust/pull/130635#issuecomment-2375462821)
|
||||
- [Legacy syntax for calling `std::arch` functions is no longer permitted to declare items or bodies (such as closures, inline consts, or async blocks).](https://github.com/rust-lang/rust/pull/130443#issuecomment-2445678945)
|
||||
- The `wasm32-unknown-emscripten` target's binary release of the standard library is now [built with the latest emsdk 3.1.68](https://github.com/rust-lang/rust/pull/131533), which fixes an ABI-incompatibility with Emscripten >= 3.1.42. If you are locally using a version of emsdk with an incompatible ABI (e.g. before 3.1.42 or a future one), you should build your code with `-Zbuild-std` to ensure that `std` uses the correct ABI.
|
||||
- [Declaring functions with a calling convention not supported on the current target now triggers a hard error](https://github.com/rust-lang/rust/pull/129935)
|
||||
- [The next-generation trait solver is now enabled for coherence, fixing multiple soundness issues](https://github.com/rust-lang/rust/pull/130654)
|
||||
|
||||
Version 1.83.0 (2024-11-28)
|
||||
==========================
|
||||
|
||||
|
|
|
|||
|
|
@ -120,8 +120,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.lower_range_end(end, e2.is_some()),
|
||||
);
|
||||
}
|
||||
// FIXME(guard_patterns): lower pattern guards to HIR
|
||||
PatKind::Guard(inner, _) => pattern = inner,
|
||||
PatKind::Guard(inner, cond) => {
|
||||
break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond));
|
||||
}
|
||||
PatKind::Slice(pats) => break self.lower_pat_slice(pats),
|
||||
PatKind::Rest => {
|
||||
// If we reach here the `..` pattern is not semantically allowed.
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
ast::ItemKind::Struct(..) => {
|
||||
ast::ItemKind::Struct(..) | ast::ItemKind::Enum(..) | ast::ItemKind::Union(..) => {
|
||||
for attr in attr::filter_by_name(&i.attrs, sym::repr) {
|
||||
for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
|
||||
if item.has_name(sym::simd) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,12 @@ use crate::RustcVersion;
|
|||
/// `since` field of the `#[stable]` attribute.
|
||||
///
|
||||
/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591).
|
||||
pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
|
||||
pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N");
|
||||
// Note that the `concat!` macro above prevents `src/tools/replace-version-placeholder` from
|
||||
// replacing the constant with the current version. Hardcoding the tool to skip this file doesn't
|
||||
// work as the file can (and at some point will) be moved around.
|
||||
//
|
||||
// Turning the `concat!` macro into a string literal will make Pietro cry. That'd be sad :(
|
||||
|
||||
/// Represents the following attributes:
|
||||
///
|
||||
|
|
|
|||
|
|
@ -213,6 +213,10 @@ borrowck_suggest_create_fresh_reborrow =
|
|||
borrowck_suggest_iterate_over_slice =
|
||||
consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop
|
||||
|
||||
borrowck_tail_expr_drop_order = relative drop order changing in Rust 2024
|
||||
.label = this temporary value will be dropped at the end of the block
|
||||
.note = consider using a `let` binding to ensure the value will live long enough
|
||||
|
||||
borrowck_ty_no_impl_copy =
|
||||
{$is_partial_move ->
|
||||
[true] partial move
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use rustc_mir_dataflow::move_paths::MoveData;
|
|||
use tracing::debug;
|
||||
|
||||
use crate::BorrowIndex;
|
||||
use crate::path_utils::allow_two_phase_borrow;
|
||||
use crate::place_ext::PlaceExt;
|
||||
|
||||
pub struct BorrowSet<'tcx> {
|
||||
|
|
@ -350,7 +349,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
|
|||
start_location, assigned_place, borrow_index,
|
||||
);
|
||||
|
||||
if !allow_two_phase_borrow(kind) {
|
||||
if !kind.allows_two_phase_borrow() {
|
||||
debug!(" -> {:?}", start_location);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1516,15 +1516,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
});
|
||||
|
||||
self.explain_why_borrow_contains_point(location, borrow, None)
|
||||
.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
Some(borrow_span),
|
||||
None,
|
||||
);
|
||||
.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
|
||||
self.suggest_copy_for_type_in_cloned_ref(&mut err, place);
|
||||
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
|
||||
if let Some(expr) = self.find_expr(borrow_span) {
|
||||
|
|
@ -1591,15 +1583,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
});
|
||||
|
||||
self.explain_why_borrow_contains_point(location, borrow, None)
|
||||
.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
None,
|
||||
None,
|
||||
);
|
||||
.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
|
||||
err
|
||||
}
|
||||
|
||||
|
|
@ -1886,9 +1870,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&self,
|
||||
&mut err,
|
||||
first_borrow_desc,
|
||||
None,
|
||||
|
|
@ -3046,15 +3028,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
|
||||
if let BorrowExplanation::MustBeValidFor { .. } = explanation {
|
||||
} else {
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
None,
|
||||
None,
|
||||
);
|
||||
explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
|
||||
}
|
||||
} else {
|
||||
err.span_label(borrow_span, "borrowed value does not live long enough");
|
||||
|
|
@ -3067,15 +3041,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
});
|
||||
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
Some(borrow_span),
|
||||
None,
|
||||
);
|
||||
explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
|
||||
}
|
||||
|
||||
err
|
||||
|
|
@ -3128,15 +3094,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
None,
|
||||
None,
|
||||
);
|
||||
explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
|
||||
|
||||
self.buffer_error(err);
|
||||
}
|
||||
|
|
@ -3309,15 +3267,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
None,
|
||||
None,
|
||||
);
|
||||
explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
|
||||
|
||||
borrow_spans.args_subdiag(&mut err, |args_span| {
|
||||
crate::session_diagnostics::CaptureArgLabel::Capture {
|
||||
|
|
@ -3808,15 +3758,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
});
|
||||
|
||||
self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
None,
|
||||
None,
|
||||
);
|
||||
self.explain_why_borrow_contains_point(location, loan, None)
|
||||
.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
|
||||
|
||||
self.explain_deref_coercion(loan, &mut err);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@
|
|||
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_errors::{Applicability, Diag, EmissionGuarantee};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_infer::infer::NllRegionVariableOrigin;
|
||||
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
|
||||
use rustc_middle::mir::{
|
||||
|
|
@ -17,14 +16,16 @@ use rustc_middle::mir::{
|
|||
};
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
|
||||
use rustc_span::{DesugaringKind, Span, Symbol, kw, sym};
|
||||
use rustc_middle::util::CallKind;
|
||||
use rustc_span::{DesugaringKind, Span, kw, sym};
|
||||
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::{RegionName, UseSpans, find_use};
|
||||
use crate::borrow_set::BorrowData;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::nll::ConstraintDescription;
|
||||
use crate::region_infer::{BlameConstraint, Cause, ExtraConstraintInfo};
|
||||
use crate::region_infer::{BlameConstraint, Cause};
|
||||
use crate::{MirBorrowckCtxt, WriteKind};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -42,7 +43,7 @@ pub(crate) enum BorrowExplanation<'tcx> {
|
|||
span: Span,
|
||||
region_name: RegionName,
|
||||
opt_place_desc: Option<String>,
|
||||
extra_info: Vec<ExtraConstraintInfo>,
|
||||
path: Vec<OutlivesConstraint<'tcx>>,
|
||||
},
|
||||
Unexplained,
|
||||
}
|
||||
|
|
@ -60,16 +61,18 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
pub(crate) fn is_explained(&self) -> bool {
|
||||
!matches!(self, BorrowExplanation::Unexplained)
|
||||
}
|
||||
pub(crate) fn add_explanation_to_diagnostic(
|
||||
pub(crate) fn add_explanation_to_diagnostic<G: EmissionGuarantee>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
local_names: &IndexSlice<Local, Option<Symbol>>,
|
||||
err: &mut Diag<'_>,
|
||||
cx: &MirBorrowckCtxt<'_, '_, 'tcx>,
|
||||
err: &mut Diag<'_, G>,
|
||||
borrow_desc: &str,
|
||||
borrow_span: Option<Span>,
|
||||
multiple_borrow_span: Option<(Span, Span)>,
|
||||
) {
|
||||
let tcx = cx.infcx.tcx;
|
||||
let body = cx.body;
|
||||
let local_names = &cx.local_names;
|
||||
|
||||
if let Some(span) = borrow_span {
|
||||
let def_id = body.source.def_id();
|
||||
if let Some(node) = tcx.hir().get_if_local(def_id)
|
||||
|
|
@ -305,7 +308,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
ref region_name,
|
||||
ref opt_place_desc,
|
||||
from_closure: _,
|
||||
ref extra_info,
|
||||
ref path,
|
||||
} => {
|
||||
region_name.highlight_region_name(err);
|
||||
|
||||
|
|
@ -327,13 +330,8 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
);
|
||||
};
|
||||
|
||||
for extra in extra_info {
|
||||
match extra {
|
||||
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
|
||||
err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
|
||||
}
|
||||
}
|
||||
}
|
||||
cx.add_placeholder_from_predicate_note(err, &path);
|
||||
cx.add_sized_or_copy_bound_info(err, category, &path);
|
||||
|
||||
if let ConstraintCategory::Cast {
|
||||
is_implicit_coercion: true,
|
||||
|
|
@ -348,10 +346,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_object_lifetime_default_note(
|
||||
fn add_object_lifetime_default_note<G: EmissionGuarantee>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
err: &mut Diag<'_, G>,
|
||||
unsize_ty: Ty<'tcx>,
|
||||
) {
|
||||
if let ty::Adt(def, args) = unsize_ty.kind() {
|
||||
|
|
@ -405,9 +403,9 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_lifetime_bound_suggestion_to_diagnostic(
|
||||
fn add_lifetime_bound_suggestion_to_diagnostic<G: EmissionGuarantee>(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
err: &mut Diag<'_, G>,
|
||||
category: &ConstraintCategory<'tcx>,
|
||||
span: Span,
|
||||
region_name: &RegionName,
|
||||
|
|
@ -434,14 +432,14 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_rewrite_if_let(
|
||||
fn suggest_rewrite_if_let<G: EmissionGuarantee>(
|
||||
tcx: TyCtxt<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
pat: &str,
|
||||
init: &hir::Expr<'_>,
|
||||
conseq: &hir::Expr<'_>,
|
||||
alt: Option<&hir::Expr<'_>>,
|
||||
err: &mut Diag<'_>,
|
||||
err: &mut Diag<'_, G>,
|
||||
) {
|
||||
let source_map = tcx.sess.source_map();
|
||||
err.span_note(
|
||||
|
|
@ -486,8 +484,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
&self,
|
||||
borrow_region: RegionVid,
|
||||
outlived_region: RegionVid,
|
||||
) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>) {
|
||||
let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint(
|
||||
) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<OutlivesConstraint<'tcx>>)
|
||||
{
|
||||
let (blame_constraint, path) = self.regioncx.best_blame_constraint(
|
||||
borrow_region,
|
||||
NllRegionVariableOrigin::FreeRegion,
|
||||
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
|
||||
|
|
@ -496,7 +495,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
|
||||
let outlived_fr_name = self.give_region_a_name(outlived_region);
|
||||
|
||||
(category, from_closure, cause.span, outlived_fr_name, extra_info)
|
||||
(category, from_closure, cause.span, outlived_fr_name, path)
|
||||
}
|
||||
|
||||
/// Returns structured explanation for *why* the borrow contains the
|
||||
|
|
@ -595,7 +594,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
|
||||
None => {
|
||||
if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
|
||||
let (category, from_closure, span, region_name, extra_info) =
|
||||
let (category, from_closure, span, region_name, path) =
|
||||
self.free_region_constraint_info(borrow_region_vid, region);
|
||||
if let Some(region_name) = region_name {
|
||||
let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref());
|
||||
|
|
@ -605,7 +604,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
span,
|
||||
region_name,
|
||||
opt_place_desc,
|
||||
extra_info,
|
||||
path,
|
||||
}
|
||||
} else {
|
||||
debug!("Could not generate a region name");
|
||||
|
|
@ -635,6 +634,39 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
// Used in a closure.
|
||||
(LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span))
|
||||
}
|
||||
// In the case that the borrowed value (probably a temporary)
|
||||
// overlaps with the method's receiver, then point at the method.
|
||||
UseSpans::FnSelfUse {
|
||||
var_span: span,
|
||||
kind: CallKind::Normal { desugaring: None, .. },
|
||||
..
|
||||
} if span
|
||||
.overlaps(self.body.local_decls[borrow.assigned_place.local].source_info.span) =>
|
||||
{
|
||||
if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } =
|
||||
&self.body.basic_blocks[location.block].terminator().kind
|
||||
{
|
||||
// Just point to the function, to reduce the chance of overlapping spans.
|
||||
let function_span = match func {
|
||||
Operand::Constant(c) => c.span,
|
||||
Operand::Copy(place) | Operand::Move(place) => {
|
||||
if let Some(l) = place.as_local() {
|
||||
let local_decl = &self.body.local_decls[l];
|
||||
if self.local_names[l].is_none() {
|
||||
local_decl.source_info.span
|
||||
} else {
|
||||
span
|
||||
}
|
||||
} else {
|
||||
span
|
||||
}
|
||||
}
|
||||
};
|
||||
(LaterUseKind::Call, function_span, None)
|
||||
} else {
|
||||
(LaterUseKind::Other, span, None)
|
||||
}
|
||||
}
|
||||
UseSpans::PatUse(span)
|
||||
| UseSpans::OtherUse(span)
|
||||
| UseSpans::FnSelfUse { var_span: span, .. } => {
|
||||
|
|
|
|||
|
|
@ -4,18 +4,20 @@ use std::collections::BTreeMap;
|
|||
|
||||
use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::{Applicability, Diag, MultiSpan};
|
||||
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan};
|
||||
use rustc_hir::def::{CtorKind, Namespace};
|
||||
use rustc_hir::{self as hir, CoroutineKind, LangItem};
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_infer::infer::BoundRegionConversionTime;
|
||||
use rustc_infer::infer::{
|
||||
BoundRegionConversionTime, NllRegionVariableOrigin, RegionVariableOrigin,
|
||||
};
|
||||
use rustc_infer::traits::SelectionError;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::{
|
||||
AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location,
|
||||
Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
|
||||
TerminatorKind,
|
||||
AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
|
||||
LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement,
|
||||
StatementKind, Terminator, TerminatorKind,
|
||||
};
|
||||
use rustc_middle::ty::print::Print;
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
|
|
@ -33,7 +35,9 @@ use tracing::debug;
|
|||
|
||||
use super::MirBorrowckCtxt;
|
||||
use super::borrow_set::BorrowData;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::nll::ConstraintDescription;
|
||||
use crate::session_diagnostics::{
|
||||
CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
|
||||
CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
|
||||
|
|
@ -619,6 +623,52 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
region.print(&mut printer).unwrap();
|
||||
printer.into_buffer()
|
||||
}
|
||||
|
||||
/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
|
||||
/// implicitly introduce an "outlives `'static`" constraint.
|
||||
fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
|
||||
&self,
|
||||
err: &mut Diag<'_, G>,
|
||||
path: &[OutlivesConstraint<'tcx>],
|
||||
) {
|
||||
let predicate_span = path.iter().find_map(|constraint| {
|
||||
let outlived = constraint.sub;
|
||||
if let Some(origin) = self.regioncx.var_infos.get(outlived)
|
||||
&& let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_)) =
|
||||
origin.origin
|
||||
&& let ConstraintCategory::Predicate(span) = constraint.category
|
||||
{
|
||||
Some(span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(span) = predicate_span {
|
||||
err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a label to region errors and borrow explanations when outlives constraints arise from
|
||||
/// proving a type implements `Sized` or `Copy`.
|
||||
fn add_sized_or_copy_bound_info<G: EmissionGuarantee>(
|
||||
&self,
|
||||
err: &mut Diag<'_, G>,
|
||||
blamed_category: ConstraintCategory<'tcx>,
|
||||
path: &[OutlivesConstraint<'tcx>],
|
||||
) {
|
||||
for sought_category in [ConstraintCategory::SizedBound, ConstraintCategory::CopyBound] {
|
||||
if sought_category != blamed_category
|
||||
&& let Some(sought_constraint) = path.iter().find(|c| c.category == sought_category)
|
||||
{
|
||||
let label = format!(
|
||||
"requirement occurs due to {}",
|
||||
sought_category.description().trim_end()
|
||||
);
|
||||
err.span_label(sought_constraint.span, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The span(s) associated to a use of a place.
|
||||
|
|
@ -661,9 +711,6 @@ impl UseSpans<'_> {
|
|||
UseSpans::ClosureUse { args_span: span, .. }
|
||||
| UseSpans::PatUse(span)
|
||||
| UseSpans::OtherUse(span) => span,
|
||||
UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
|
||||
fn_call_span
|
||||
}
|
||||
UseSpans::FnSelfUse { var_span, .. } => var_span,
|
||||
}
|
||||
}
|
||||
|
|
@ -674,9 +721,6 @@ impl UseSpans<'_> {
|
|||
UseSpans::ClosureUse { path_span: span, .. }
|
||||
| UseSpans::PatUse(span)
|
||||
| UseSpans::OtherUse(span) => span,
|
||||
UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
|
||||
fn_call_span
|
||||
}
|
||||
UseSpans::FnSelfUse { var_span, .. } => var_span,
|
||||
}
|
||||
}
|
||||
|
|
@ -687,9 +731,6 @@ impl UseSpans<'_> {
|
|||
UseSpans::ClosureUse { capture_kind_span: span, .. }
|
||||
| UseSpans::PatUse(span)
|
||||
| UseSpans::OtherUse(span) => span,
|
||||
UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
|
||||
fn_call_span
|
||||
}
|
||||
UseSpans::FnSelfUse { var_span, .. } => var_span,
|
||||
}
|
||||
}
|
||||
|
|
@ -1001,6 +1042,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
kind,
|
||||
};
|
||||
}
|
||||
|
||||
normal_ret
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use rustc_hir::intravisit::Visitor;
|
|||
use rustc_hir::{self as hir, BindingMode, ByRef, Node};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::hir::place::PlaceBase;
|
||||
use rustc_middle::mir::visit::PlaceContext;
|
||||
use rustc_middle::mir::{
|
||||
self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place,
|
||||
PlaceRef, ProjectionElem,
|
||||
|
|
@ -22,7 +23,6 @@ use rustc_trait_selection::traits;
|
|||
use tracing::debug;
|
||||
|
||||
use crate::diagnostics::BorrowedContentSource;
|
||||
use crate::util::FindAssignments;
|
||||
use crate::{MirBorrowckCtxt, session_diagnostics};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
|
|
@ -1088,6 +1088,38 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
|
||||
/// locations.
|
||||
fn find_assignments(&self, local: Local) -> Vec<Location> {
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
|
||||
struct FindLocalAssignmentVisitor {
|
||||
needle: Local,
|
||||
locations: Vec<Location>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
|
||||
fn visit_local(
|
||||
&mut self,
|
||||
local: Local,
|
||||
place_context: PlaceContext,
|
||||
location: Location,
|
||||
) {
|
||||
if self.needle != local {
|
||||
return;
|
||||
}
|
||||
|
||||
if place_context.is_place_assignment() {
|
||||
self.locations.push(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
|
||||
visitor.visit_body(self.body);
|
||||
visitor.locations
|
||||
}
|
||||
|
||||
fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
|
||||
|
|
@ -1121,7 +1153,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
})) => {
|
||||
// check if the RHS is from desugaring
|
||||
let opt_assignment_rhs_span =
|
||||
self.body.find_assignments(local).first().map(|&location| {
|
||||
self.find_assignments(local).first().map(|&location| {
|
||||
if let Some(mir::Statement {
|
||||
source_info: _,
|
||||
kind:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate};
|
|||
use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::hir::place::PlaceBase;
|
||||
use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
|
||||
use rustc_middle::mir::{AnnotationSource, ConstraintCategory, ReturnConstraint};
|
||||
use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor};
|
||||
use rustc_span::{Ident, Span, kw};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
|
|
@ -29,7 +29,7 @@ use tracing::{debug, instrument, trace};
|
|||
use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource};
|
||||
use crate::nll::ConstraintDescription;
|
||||
use crate::region_infer::values::RegionElement;
|
||||
use crate::region_infer::{BlameConstraint, ExtraConstraintInfo, TypeTest};
|
||||
use crate::region_infer::{BlameConstraint, TypeTest};
|
||||
use crate::session_diagnostics::{
|
||||
FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr,
|
||||
LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote,
|
||||
|
|
@ -49,8 +49,8 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
|
|||
ConstraintCategory::Cast { is_implicit_coercion: false, .. } => "cast ",
|
||||
ConstraintCategory::Cast { is_implicit_coercion: true, .. } => "coercion ",
|
||||
ConstraintCategory::CallArgument(_) => "argument ",
|
||||
ConstraintCategory::TypeAnnotation => "type annotation ",
|
||||
ConstraintCategory::ClosureBounds => "closure body ",
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => "generic argument ",
|
||||
ConstraintCategory::TypeAnnotation(_) => "type annotation ",
|
||||
ConstraintCategory::SizedBound => "proving this value is `Sized` ",
|
||||
ConstraintCategory::CopyBound => "copying this value ",
|
||||
ConstraintCategory::OpaqueType => "opaque type ",
|
||||
|
|
@ -440,10 +440,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
) {
|
||||
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||
|
||||
let (blame_constraint, extra_info) =
|
||||
self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
|
||||
self.regioncx.provides_universal_region(r, fr, outlived_fr)
|
||||
});
|
||||
let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
|
||||
self.regioncx.provides_universal_region(r, fr, outlived_fr)
|
||||
});
|
||||
let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
|
||||
|
||||
debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
|
||||
|
|
@ -554,13 +553,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
for extra in extra_info {
|
||||
match extra {
|
||||
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
|
||||
diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
|
||||
}
|
||||
}
|
||||
}
|
||||
self.add_placeholder_from_predicate_note(&mut diag, &path);
|
||||
self.add_sized_or_copy_bound_info(&mut diag, category, &path);
|
||||
|
||||
self.buffer_error(diag);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::fmt::{self, Display};
|
|||
use std::iter;
|
||||
|
||||
use rustc_data_structures::fx::IndexEntry;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_errors::{Diag, EmissionGuarantee};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_middle::ty::print::RegionHighlightMode;
|
||||
|
|
@ -108,7 +108,7 @@ impl RegionName {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn highlight_region_name(&self, diag: &mut Diag<'_>) {
|
||||
pub(crate) fn highlight_region_name<G: EmissionGuarantee>(&self, diag: &mut Diag<'_, G>) {
|
||||
match &self.source {
|
||||
RegionNameSource::NamedLateParamRegion(span)
|
||||
| RegionNameSource::NamedEarlyParamRegion(span) => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(file_buffered)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
|
@ -15,14 +16,17 @@
|
|||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::ops::{ControlFlow, Deref};
|
||||
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_errors::LintDiagnostic;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::CRATE_HIR_ID;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::bit_set::{BitSet, MixedBitSet};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
|
|
@ -42,7 +46,7 @@ use rustc_mir_dataflow::move_paths::{
|
|||
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
|
||||
};
|
||||
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
|
||||
use rustc_session::lint::builtin::UNUSED_MUT;
|
||||
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use smallvec::SmallVec;
|
||||
use tracing::{debug, instrument};
|
||||
|
|
@ -81,7 +85,6 @@ mod session_diagnostics;
|
|||
mod type_check;
|
||||
mod universal_regions;
|
||||
mod used_muts;
|
||||
mod util;
|
||||
|
||||
/// A public API provided for the Rust compiler consumers.
|
||||
pub mod consumers;
|
||||
|
|
@ -636,9 +639,11 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
|
|||
| StatementKind::Coverage(..)
|
||||
// These do not actually affect borrowck
|
||||
| StatementKind::ConstEvalCounter
|
||||
// This do not affect borrowck
|
||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
||||
| StatementKind::StorageLive(..) => {}
|
||||
// This does not affect borrowck
|
||||
StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => {
|
||||
self.check_backward_incompatible_drop(location, (**place, span), state);
|
||||
}
|
||||
StatementKind::StorageDead(local) => {
|
||||
self.access_place(
|
||||
location,
|
||||
|
|
@ -1007,6 +1012,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn borrows_in_scope<'s>(
|
||||
&self,
|
||||
location: Location,
|
||||
state: &'s BorrowckDomain,
|
||||
) -> Cow<'s, BitSet<BorrowIndex>> {
|
||||
if let Some(polonius) = &self.polonius_output {
|
||||
// Use polonius output if it has been enabled.
|
||||
let location = self.location_table.start_index(location);
|
||||
let mut polonius_output = BitSet::new_empty(self.borrow_set.len());
|
||||
for &idx in polonius.errors_at(location) {
|
||||
polonius_output.insert(idx);
|
||||
}
|
||||
Cow::Owned(polonius_output)
|
||||
} else {
|
||||
Cow::Borrowed(&state.borrows)
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, state))]
|
||||
fn check_access_for_conflict(
|
||||
&mut self,
|
||||
|
|
@ -1018,18 +1041,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
) -> bool {
|
||||
let mut error_reported = false;
|
||||
|
||||
// Use polonius output if it has been enabled.
|
||||
let mut polonius_output;
|
||||
let borrows_in_scope = if let Some(polonius) = &self.polonius_output {
|
||||
let location = self.location_table.start_index(location);
|
||||
polonius_output = BitSet::new_empty(self.borrow_set.len());
|
||||
for &idx in polonius.errors_at(location) {
|
||||
polonius_output.insert(idx);
|
||||
}
|
||||
&polonius_output
|
||||
} else {
|
||||
&state.borrows
|
||||
};
|
||||
let borrows_in_scope = self.borrows_in_scope(location, state);
|
||||
|
||||
each_borrow_involving_path(
|
||||
self,
|
||||
|
|
@ -1054,31 +1066,31 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
rw,
|
||||
(borrow_index, borrow),
|
||||
);
|
||||
Control::Continue
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
(Read(_), BorrowKind::Shared | BorrowKind::Fake(_))
|
||||
| (
|
||||
Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))),
|
||||
BorrowKind::Mut { .. },
|
||||
) => Control::Continue,
|
||||
) => ControlFlow::Continue(()),
|
||||
|
||||
(Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => {
|
||||
// This used to be a future compatibility warning (to be
|
||||
// disallowed on NLL). See rust-lang/rust#56254
|
||||
Control::Continue
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
(Write(WriteKind::Move), BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
|
||||
// Handled by initialization checks.
|
||||
Control::Continue
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
(Read(kind), BorrowKind::Mut { .. }) => {
|
||||
// Reading from mere reservations of mutable-borrows is OK.
|
||||
if !is_active(this.dominators(), borrow, location) {
|
||||
assert!(allow_two_phase_borrow(borrow.kind));
|
||||
return Control::Continue;
|
||||
assert!(borrow.kind.allows_two_phase_borrow());
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
error_reported = true;
|
||||
|
|
@ -1094,7 +1106,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
this.buffer_error(err);
|
||||
}
|
||||
}
|
||||
Control::Break
|
||||
ControlFlow::Break(())
|
||||
}
|
||||
|
||||
(Reservation(kind) | Activation(kind, _) | Write(kind), _) => {
|
||||
|
|
@ -1141,7 +1153,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
this.report_illegal_mutation_of_borrowed(location, place_span, borrow)
|
||||
}
|
||||
}
|
||||
Control::Break
|
||||
ControlFlow::Break(())
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -1149,6 +1161,61 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
error_reported
|
||||
}
|
||||
|
||||
/// Through #123739, backward incompatible drops (BIDs) are introduced.
|
||||
/// We would like to emit lints whether borrow checking fails at these future drop locations.
|
||||
#[instrument(level = "debug", skip(self, state))]
|
||||
fn check_backward_incompatible_drop(
|
||||
&mut self,
|
||||
location: Location,
|
||||
(place, place_span): (Place<'tcx>, Span),
|
||||
state: &BorrowckDomain,
|
||||
) {
|
||||
let tcx = self.infcx.tcx;
|
||||
// If this type does not need `Drop`, then treat it like a `StorageDead`.
|
||||
// This is needed because we track the borrows of refs to thread locals,
|
||||
// and we'll ICE because we don't track borrows behind shared references.
|
||||
let sd = if place.ty(self.body, tcx).ty.needs_drop(tcx, self.body.typing_env(tcx)) {
|
||||
AccessDepth::Drop
|
||||
} else {
|
||||
AccessDepth::Shallow(None)
|
||||
};
|
||||
|
||||
let borrows_in_scope = self.borrows_in_scope(location, state);
|
||||
|
||||
// This is a very simplified version of `Self::check_access_for_conflict`.
|
||||
// We are here checking on BIDs and specifically still-live borrows of data involving the BIDs.
|
||||
each_borrow_involving_path(
|
||||
self,
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
(sd, place),
|
||||
self.borrow_set,
|
||||
|borrow_index| borrows_in_scope.contains(borrow_index),
|
||||
|this, _borrow_index, borrow| {
|
||||
if matches!(borrow.kind, BorrowKind::Fake(_)) {
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
let borrowed = this.retrieve_borrow_spans(borrow).var_or_use_path_span();
|
||||
let explain = this.explain_why_borrow_contains_point(
|
||||
location,
|
||||
borrow,
|
||||
Some((WriteKind::StorageDeadOrDrop, place)),
|
||||
);
|
||||
this.infcx.tcx.node_span_lint(
|
||||
TAIL_EXPR_DROP_ORDER,
|
||||
CRATE_HIR_ID,
|
||||
borrowed,
|
||||
|diag| {
|
||||
session_diagnostics::TailExprDropOrder { borrowed }.decorate_lint(diag);
|
||||
explain.add_explanation_to_diagnostic(&this, diag, "", None, None);
|
||||
},
|
||||
);
|
||||
// We may stop at the first case
|
||||
ControlFlow::Break(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn mutate_place(
|
||||
&mut self,
|
||||
location: Location,
|
||||
|
|
@ -1185,7 +1252,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
}
|
||||
BorrowKind::Mut { .. } => {
|
||||
let wk = WriteKind::MutableBorrow(bk);
|
||||
if allow_two_phase_borrow(bk) {
|
||||
if bk.allows_two_phase_borrow() {
|
||||
(Deep, Reservation(wk))
|
||||
} else {
|
||||
(Deep, Write(wk))
|
||||
|
|
|
|||
|
|
@ -142,9 +142,9 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
|
||||
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives
|
||||
// constraints.
|
||||
let localized_outlives_constraints = polonius_context
|
||||
.as_mut()
|
||||
.map(|polonius_context| polonius_context.create_localized_constraints(&mut regioncx, body));
|
||||
let localized_outlives_constraints = polonius_context.as_mut().map(|polonius_context| {
|
||||
polonius_context.create_localized_constraints(infcx.tcx, ®ioncx, body)
|
||||
});
|
||||
|
||||
// If requested: dump NLL facts, and run legacy polonius analysis.
|
||||
let polonius_output = all_facts.as_ref().and_then(|all_facts| {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,14 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_middle::mir::{BasicBlock, Body, BorrowKind, Location, Place, PlaceRef, ProjectionElem};
|
||||
use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
|
||||
use crate::{AccessDepth, BorrowIndex, places_conflict};
|
||||
|
||||
/// Returns `true` if the borrow represented by `kind` is
|
||||
/// allowed to be split into separate Reservation and
|
||||
/// Activation phases.
|
||||
pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool {
|
||||
kind.allows_two_phase_borrow()
|
||||
}
|
||||
|
||||
/// Control for the path borrow checking code
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub(super) enum Control {
|
||||
Continue,
|
||||
Break,
|
||||
}
|
||||
|
||||
/// Encapsulates the idea of iterating over every borrow that involves a particular path
|
||||
pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
|
||||
s: &mut S,
|
||||
|
|
@ -31,7 +19,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
|
|||
is_candidate: I,
|
||||
mut op: F,
|
||||
) where
|
||||
F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control,
|
||||
F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> ControlFlow<()>,
|
||||
I: Fn(BorrowIndex) -> bool,
|
||||
{
|
||||
let (access, place) = access_place;
|
||||
|
|
@ -62,7 +50,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
|
|||
i, borrowed, place, access
|
||||
);
|
||||
let ctrl = op(s, i, borrowed);
|
||||
if ctrl == Control::Break {
|
||||
if matches!(ctrl, ControlFlow::Break(_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
|
|
@ -260,7 +262,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
}
|
||||
BorrowKind::Mut { .. } => {
|
||||
let wk = WriteKind::MutableBorrow(bk);
|
||||
if allow_two_phase_borrow(bk) {
|
||||
if bk.allows_two_phase_borrow() {
|
||||
(Deep, Reservation(wk))
|
||||
} else {
|
||||
(Deep, Write(wk))
|
||||
|
|
@ -378,8 +380,8 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
// Reading from mere reservations of mutable-borrows is OK.
|
||||
if !is_active(this.dominators, borrow, location) {
|
||||
// If the borrow isn't active yet, reads don't invalidate it
|
||||
assert!(allow_two_phase_borrow(borrow.kind));
|
||||
return Control::Continue;
|
||||
assert!(borrow.kind.allows_two_phase_borrow());
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
// Unique and mutable borrows are invalidated by reads from any
|
||||
|
|
@ -395,7 +397,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
this.emit_loan_invalidated_at(borrow_index, location);
|
||||
}
|
||||
}
|
||||
Control::Continue
|
||||
ControlFlow::Continue(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,21 +37,20 @@ mod constraints;
|
|||
mod dump;
|
||||
pub(crate) mod legacy;
|
||||
mod liveness_constraints;
|
||||
mod typeck_constraints;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_index::bit_set::SparseBitMatrix;
|
||||
use rustc_middle::mir::{Body, Location};
|
||||
use rustc_middle::ty::RegionVid;
|
||||
use rustc_middle::mir::Body;
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
pub(crate) use self::constraints::*;
|
||||
pub(crate) use self::dump::dump_polonius_mir;
|
||||
use self::liveness_constraints::create_liveness_constraints;
|
||||
use self::typeck_constraints::convert_typeck_constraints;
|
||||
use crate::RegionInferenceContext;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
|
||||
/// This struct holds the data needed to create the Polonius localized constraints.
|
||||
pub(crate) struct PoloniusContext {
|
||||
|
|
@ -88,14 +87,17 @@ impl PoloniusContext {
|
|||
/// - encoding liveness constraints
|
||||
pub(crate) fn create_localized_constraints<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
) -> LocalizedOutlivesConstraintSet {
|
||||
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
|
||||
convert_typeck_constraints(
|
||||
tcx,
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.outlives_constraints(),
|
||||
regioncx.universal_regions(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
|
||||
|
|
@ -117,38 +119,3 @@ impl PoloniusContext {
|
|||
localized_outlives_constraints
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
|
||||
/// location where effects start to be visible).
|
||||
fn convert_typeck_constraints<'tcx>(
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
for outlives_constraint in outlives_constraints {
|
||||
match outlives_constraint.locations {
|
||||
Locations::All(_) => {
|
||||
// For now, turn logical constraints holding at all points into physical edges at
|
||||
// every point in the graph.
|
||||
// FIXME: encode this into *traversal* instead.
|
||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
||||
let statement_count = bb.statements.len();
|
||||
for statement_index in 0..=statement_count {
|
||||
let current_location = Location { block, statement_index };
|
||||
let current_point = liveness.point_from_location(current_location);
|
||||
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from: current_point,
|
||||
target: outlives_constraint.sub,
|
||||
to: current_point,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
241
compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
Normal file
241
compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::mir::{Body, Location, Statement, StatementKind, Terminator, TerminatorKind};
|
||||
use rustc_middle::ty::{TyCtxt, TypeVisitable};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
use super::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet};
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
|
||||
/// location where effects start to be visible).
|
||||
pub(super) fn convert_typeck_constraints<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
for outlives_constraint in outlives_constraints {
|
||||
match outlives_constraint.locations {
|
||||
Locations::All(_) => {
|
||||
// For now, turn logical constraints holding at all points into physical edges at
|
||||
// every point in the graph.
|
||||
// FIXME: encode this into *traversal* instead.
|
||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
||||
let statement_count = bb.statements.len();
|
||||
for statement_index in 0..=statement_count {
|
||||
let current_location = Location { block, statement_index };
|
||||
let current_point = liveness.point_from_location(current_location);
|
||||
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from: current_point,
|
||||
target: outlives_constraint.sub,
|
||||
to: current_point,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Locations::Single(location) => {
|
||||
// This constraint is marked as holding at one location, we localize it to that
|
||||
// location or its successor, depending on the corresponding MIR
|
||||
// statement/terminator. Unfortunately, they all show up from typeck as coming "on
|
||||
// entry", so for now we modify them to take effects that should apply "on exit"
|
||||
// into account.
|
||||
//
|
||||
// FIXME: this approach is subtle, complicated, and hard to test, so we should track
|
||||
// this information better in MIR typeck instead, for example with a new `Locations`
|
||||
// variant that contains which node is crossing over between entry and exit.
|
||||
let point = liveness.point_from_location(location);
|
||||
let localized_constraint = if let Some(stmt) =
|
||||
body[location.block].statements.get(location.statement_index)
|
||||
{
|
||||
localize_statement_constraint(
|
||||
tcx,
|
||||
body,
|
||||
stmt,
|
||||
liveness,
|
||||
&outlives_constraint,
|
||||
location,
|
||||
point,
|
||||
universal_regions,
|
||||
)
|
||||
} else {
|
||||
assert_eq!(location.statement_index, body[location.block].statements.len());
|
||||
let terminator = body[location.block].terminator();
|
||||
localize_terminator_constraint(
|
||||
tcx,
|
||||
body,
|
||||
terminator,
|
||||
liveness,
|
||||
&outlives_constraint,
|
||||
point,
|
||||
universal_regions,
|
||||
)
|
||||
};
|
||||
localized_outlives_constraints.push(localized_constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For a given outlives constraint arising from a MIR statement, localize the constraint with the
|
||||
/// needed CFG `from`-`to` intra-block nodes.
|
||||
fn localize_statement_constraint<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
stmt: &Statement<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraint: &OutlivesConstraint<'tcx>,
|
||||
current_location: Location,
|
||||
current_point: PointIndex,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> LocalizedOutlivesConstraint {
|
||||
match &stmt.kind {
|
||||
StatementKind::Assign(box (lhs, rhs)) => {
|
||||
// To create localized outlives constraints without midpoints, we rely on the property
|
||||
// that no input regions from the RHS of the assignment will flow into themselves: they
|
||||
// should not appear in the output regions in the LHS. We believe this to be true by
|
||||
// construction of the MIR, via temporaries, and assert it here.
|
||||
//
|
||||
// We think we don't need midpoints because:
|
||||
// - every LHS Place has a unique set of regions that don't appear elsewhere
|
||||
// - this implies that for them to be part of the RHS, the same Place must be read and
|
||||
// written
|
||||
// - and that should be impossible in MIR
|
||||
//
|
||||
// When we have a more complete implementation in the future, tested with crater, etc,
|
||||
// we can relax this to a debug assert instead, or remove it.
|
||||
assert!(
|
||||
{
|
||||
let mut lhs_regions = FxHashSet::default();
|
||||
tcx.for_each_free_region(lhs, |region| {
|
||||
let region = universal_regions.to_region_vid(region);
|
||||
lhs_regions.insert(region);
|
||||
});
|
||||
|
||||
let mut rhs_regions = FxHashSet::default();
|
||||
tcx.for_each_free_region(rhs, |region| {
|
||||
let region = universal_regions.to_region_vid(region);
|
||||
rhs_regions.insert(region);
|
||||
});
|
||||
|
||||
// The intersection between LHS and RHS regions should be empty.
|
||||
lhs_regions.is_disjoint(&rhs_regions)
|
||||
},
|
||||
"there should be no common regions between the LHS and RHS of an assignment"
|
||||
);
|
||||
|
||||
// As mentioned earlier, we should be tracking these better upstream but: we want to
|
||||
// relate the types on entry to the type of the place on exit. That is, outlives
|
||||
// constraints on the RHS are on entry, and outlives constraints to/from the LHS are on
|
||||
// exit (i.e. on entry to the successor location).
|
||||
let lhs_ty = body.local_decls[lhs.local].ty;
|
||||
let successor_location = Location {
|
||||
block: current_location.block,
|
||||
statement_index: current_location.statement_index + 1,
|
||||
};
|
||||
let successor_point = liveness.point_from_location(successor_location);
|
||||
compute_constraint_direction(
|
||||
tcx,
|
||||
outlives_constraint,
|
||||
&lhs_ty,
|
||||
current_point,
|
||||
successor_point,
|
||||
universal_regions,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// For the other cases, we localize an outlives constraint to where it arises.
|
||||
LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from: current_point,
|
||||
target: outlives_constraint.sub,
|
||||
to: current_point,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For a given outlives constraint arising from a MIR terminator, localize the constraint with the
|
||||
/// needed CFG `from`-`to` inter-block nodes.
|
||||
fn localize_terminator_constraint<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
terminator: &Terminator<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraint: &OutlivesConstraint<'tcx>,
|
||||
current_point: PointIndex,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> LocalizedOutlivesConstraint {
|
||||
// FIXME: check if other terminators need the same handling as `Call`s, in particular
|
||||
// Assert/Yield/Drop. A handful of tests are failing with Drop related issues, as well as some
|
||||
// coroutine tests, and that may be why.
|
||||
match &terminator.kind {
|
||||
// FIXME: also handle diverging calls.
|
||||
TerminatorKind::Call { destination, target: Some(target), .. } => {
|
||||
// Calls are similar to assignments, and thus follow the same pattern. If there is a
|
||||
// target for the call we also relate what flows into the destination here to entry to
|
||||
// that successor.
|
||||
let destination_ty = destination.ty(&body.local_decls, tcx);
|
||||
let successor_location = Location { block: *target, statement_index: 0 };
|
||||
let successor_point = liveness.point_from_location(successor_location);
|
||||
compute_constraint_direction(
|
||||
tcx,
|
||||
outlives_constraint,
|
||||
&destination_ty,
|
||||
current_point,
|
||||
successor_point,
|
||||
universal_regions,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// Typeck constraints guide loans between regions at the current point, so we do that in
|
||||
// the general case, and liveness will take care of making them flow to the terminator's
|
||||
// successors.
|
||||
LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from: current_point,
|
||||
target: outlives_constraint.sub,
|
||||
to: current_point,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// For a given outlives constraint and CFG edge, returns the localized constraint with the
|
||||
/// appropriate `from`-`to` direction. This is computed according to whether the constraint flows to
|
||||
/// or from a free region in the given `value`, some kind of result for an effectful operation, like
|
||||
/// the LHS of an assignment.
|
||||
fn compute_constraint_direction<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
outlives_constraint: &OutlivesConstraint<'tcx>,
|
||||
value: &impl TypeVisitable<TyCtxt<'tcx>>,
|
||||
current_point: PointIndex,
|
||||
successor_point: PointIndex,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> LocalizedOutlivesConstraint {
|
||||
let mut to = current_point;
|
||||
let mut from = current_point;
|
||||
tcx.for_each_free_region(value, |region| {
|
||||
let region = universal_regions.to_region_vid(region);
|
||||
if region == outlives_constraint.sub {
|
||||
// This constraint flows into the result, its effects start becoming visible on exit.
|
||||
to = successor_point;
|
||||
} else if region == outlives_constraint.sup {
|
||||
// This constraint flows from the result, its effects start becoming visible on exit.
|
||||
from = successor_point;
|
||||
}
|
||||
});
|
||||
|
||||
LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from,
|
||||
target: outlives_constraint.sub,
|
||||
to,
|
||||
}
|
||||
}
|
||||
|
|
@ -13,15 +13,16 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound,
|
|||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::{
|
||||
BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy,
|
||||
ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint,
|
||||
TerminatorKind,
|
||||
AnnotationSource, BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject,
|
||||
ClosureOutlivesSubjectTy, ClosureRegionRequirements, ConstraintCategory, Local, Location,
|
||||
ReturnConstraint, TerminatorKind,
|
||||
};
|
||||
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc_middle::ty::fold::fold_regions;
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex};
|
||||
use rustc_mir_dataflow::points::DenseLocationMap;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
use crate::BorrowckInferCtxt;
|
||||
|
|
@ -315,11 +316,6 @@ enum Trace<'tcx> {
|
|||
NotVisited,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) enum ExtraConstraintInfo {
|
||||
PlaceholderFromPredicate(Span),
|
||||
}
|
||||
|
||||
#[instrument(skip(infcx, sccs), level = "debug")]
|
||||
fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
|
||||
use crate::renumber::RegionCtxt;
|
||||
|
|
@ -978,7 +974,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'tcx>>,
|
||||
) -> bool {
|
||||
let tcx = infcx.tcx;
|
||||
let TypeTest { generic_kind, lower_bound, span: blame_span, ref verify_bound } = *type_test;
|
||||
let TypeTest { generic_kind, lower_bound, span: blame_span, verify_bound: _ } = *type_test;
|
||||
|
||||
let generic_ty = generic_kind.to_ty(tcx);
|
||||
let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else {
|
||||
|
|
@ -1016,25 +1012,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// For each region outlived by lower_bound find a non-local,
|
||||
// universal region (it may be the same region) and add it to
|
||||
// `ClosureOutlivesRequirement`.
|
||||
let mut found_outlived_universal_region = false;
|
||||
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
|
||||
found_outlived_universal_region = true;
|
||||
debug!("universal_region_outlived_by ur={:?}", ur);
|
||||
// Check whether we can already prove that the "subject" outlives `ur`.
|
||||
// If so, we don't have to propagate this requirement to our caller.
|
||||
//
|
||||
// To continue the example from the function, if we are trying to promote
|
||||
// a requirement that `T: 'X`, and we know that `'X = '1 + '2` (i.e., the union
|
||||
// `'1` and `'2`), then in this loop `ur` will be `'1` (and `'2`). So here
|
||||
// we check whether `T: '1` is something we *can* prove. If so, no need
|
||||
// to propagate that requirement.
|
||||
//
|
||||
// This is needed because -- particularly in the case
|
||||
// where `ur` is a local bound -- we are sometimes in a
|
||||
// position to prove things that our caller cannot. See
|
||||
// #53570 for an example.
|
||||
if self.eval_verify_bound(infcx, generic_ty, ur, &verify_bound) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur);
|
||||
debug!(?non_local_ub);
|
||||
|
||||
|
|
@ -1056,6 +1037,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
propagated_outlives_requirements.push(requirement);
|
||||
}
|
||||
}
|
||||
// If we succeed to promote the subject, i.e. it only contains non-local regions,
|
||||
// and fail to prove the type test inside of the closure, the `lower_bound` has to
|
||||
// also be at least as large as some universal region, as the type test is otherwise
|
||||
// trivial.
|
||||
assert!(found_outlived_universal_region);
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -1948,7 +1934,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
from_region: RegionVid,
|
||||
from_region_origin: NllRegionVariableOrigin,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
|
||||
) -> (BlameConstraint<'tcx>, Vec<OutlivesConstraint<'tcx>>) {
|
||||
// Find all paths
|
||||
let (path, target_region) = self
|
||||
.find_constraint_paths_between_regions(from_region, target_test)
|
||||
|
|
@ -1970,25 +1956,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
let mut extra_info = vec![];
|
||||
for constraint in path.iter() {
|
||||
let outlived = constraint.sub;
|
||||
let Some(origin) = self.var_infos.get(outlived) else {
|
||||
continue;
|
||||
};
|
||||
let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(p)) = origin.origin
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
debug!(?constraint, ?p);
|
||||
let ConstraintCategory::Predicate(span) = constraint.category else {
|
||||
continue;
|
||||
};
|
||||
extra_info.push(ExtraConstraintInfo::PlaceholderFromPredicate(span));
|
||||
// We only want to point to one
|
||||
break;
|
||||
}
|
||||
|
||||
// We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint.
|
||||
// Instead, we use it to produce an improved `ObligationCauseCode`.
|
||||
// FIXME - determine what we should do if we encounter multiple
|
||||
|
|
@ -2007,42 +1974,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
})
|
||||
.unwrap_or_else(|| ObligationCauseCode::Misc);
|
||||
|
||||
// Classify each of the constraints along the path.
|
||||
let mut categorized_path: Vec<BlameConstraint<'tcx>> = path
|
||||
.iter()
|
||||
.map(|constraint| BlameConstraint {
|
||||
category: constraint.category,
|
||||
from_closure: constraint.from_closure,
|
||||
cause: ObligationCause::new(constraint.span, CRATE_DEF_ID, cause_code.clone()),
|
||||
variance_info: constraint.variance_info,
|
||||
})
|
||||
.collect();
|
||||
debug!("categorized_path={:#?}", categorized_path);
|
||||
|
||||
// To find the best span to cite, we first try to look for the
|
||||
// final constraint that is interesting and where the `sup` is
|
||||
// not unified with the ultimate target region. The reason
|
||||
// for this is that we have a chain of constraints that lead
|
||||
// from the source to the target region, something like:
|
||||
//
|
||||
// '0: '1 ('0 is the source)
|
||||
// '1: '2
|
||||
// '2: '3
|
||||
// '3: '4
|
||||
// '4: '5
|
||||
// '5: '6 ('6 is the target)
|
||||
//
|
||||
// Some of those regions are unified with `'6` (in the same
|
||||
// SCC). We want to screen those out. After that point, the
|
||||
// "closest" constraint we have to the end is going to be the
|
||||
// most likely to be the point where the value escapes -- but
|
||||
// we still want to screen for an "interesting" point to
|
||||
// highlight (e.g., a call site or something).
|
||||
let target_scc = self.constraint_sccs.scc(target_region);
|
||||
let mut range = 0..path.len();
|
||||
|
||||
// As noted above, when reporting an error, there is typically a chain of constraints
|
||||
// leading from some "source" region which must outlive some "target" region.
|
||||
// When reporting an error, there is typically a chain of constraints leading from some
|
||||
// "source" region which must outlive some "target" region.
|
||||
// In most cases, we prefer to "blame" the constraints closer to the target --
|
||||
// but there is one exception. When constraints arise from higher-ranked subtyping,
|
||||
// we generally prefer to blame the source value,
|
||||
|
|
@ -2083,78 +2016,114 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
| NllRegionVariableOrigin::Existential { from_forall: true } => false,
|
||||
};
|
||||
|
||||
let find_region = |i: &usize| {
|
||||
let constraint = &path[*i];
|
||||
|
||||
let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
|
||||
|
||||
if blame_source {
|
||||
match categorized_path[*i].category {
|
||||
ConstraintCategory::OpaqueType
|
||||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal
|
||||
| ConstraintCategory::Predicate(_) => false,
|
||||
ConstraintCategory::TypeAnnotation
|
||||
| ConstraintCategory::Return(_)
|
||||
| ConstraintCategory::Yield => true,
|
||||
_ => constraint_sup_scc != target_scc,
|
||||
}
|
||||
// To pick a constraint to blame, we organize constraints by how interesting we expect them
|
||||
// to be in diagnostics, then pick the most interesting one closest to either the source or
|
||||
// the target on our constraint path.
|
||||
let constraint_interest = |constraint: &OutlivesConstraint<'tcx>| {
|
||||
// Try to avoid blaming constraints from desugarings, since they may not clearly match
|
||||
// match what users have written. As an exception, allow blaming returns generated by
|
||||
// `?` desugaring, since the correspondence is fairly clear.
|
||||
let category = if let Some(kind) = constraint.span.desugaring_kind()
|
||||
&& (kind != DesugaringKind::QuestionMark
|
||||
|| !matches!(constraint.category, ConstraintCategory::Return(_)))
|
||||
{
|
||||
ConstraintCategory::Boring
|
||||
} else {
|
||||
!matches!(
|
||||
categorized_path[*i].category,
|
||||
ConstraintCategory::OpaqueType
|
||||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal
|
||||
| ConstraintCategory::Predicate(_)
|
||||
constraint.category
|
||||
};
|
||||
|
||||
match category {
|
||||
// Returns usually provide a type to blame and have specially written diagnostics,
|
||||
// so prioritize them.
|
||||
ConstraintCategory::Return(_) => 0,
|
||||
// Unsizing coercions are interesting, since we have a note for that:
|
||||
// `BorrowExplanation::add_object_lifetime_default_note`.
|
||||
// FIXME(dianne): That note shouldn't depend on a coercion being blamed; see issue
|
||||
// #131008 for an example of where we currently don't emit it but should.
|
||||
// Once the note is handled properly, this case should be removed. Until then, it
|
||||
// should be as limited as possible; the note is prone to false positives and this
|
||||
// constraint usually isn't best to blame.
|
||||
ConstraintCategory::Cast {
|
||||
unsize_to: Some(unsize_ty),
|
||||
is_implicit_coercion: true,
|
||||
} if target_region == self.universal_regions().fr_static
|
||||
// Mirror the note's condition, to minimize how often this diverts blame.
|
||||
&& let ty::Adt(_, args) = unsize_ty.kind()
|
||||
&& args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait()))
|
||||
// Mimic old logic for this, to minimize false positives in tests.
|
||||
&& !path
|
||||
.iter()
|
||||
.any(|c| matches!(c.category, ConstraintCategory::TypeAnnotation(_))) =>
|
||||
{
|
||||
1
|
||||
}
|
||||
// Between other interesting constraints, order by their position on the `path`.
|
||||
ConstraintCategory::Yield
|
||||
| ConstraintCategory::UseAsConst
|
||||
| ConstraintCategory::UseAsStatic
|
||||
| ConstraintCategory::TypeAnnotation(
|
||||
AnnotationSource::Ascription
|
||||
| AnnotationSource::Declaration
|
||||
| AnnotationSource::OpaqueCast,
|
||||
)
|
||||
| ConstraintCategory::Cast { .. }
|
||||
| ConstraintCategory::CallArgument(_)
|
||||
| ConstraintCategory::CopyBound
|
||||
| ConstraintCategory::SizedBound
|
||||
| ConstraintCategory::Assignment
|
||||
| ConstraintCategory::Usage
|
||||
| ConstraintCategory::ClosureUpvar(_) => 2,
|
||||
// Generic arguments are unlikely to be what relates regions together
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => 3,
|
||||
// We handle predicates and opaque types specially; don't prioritize them here.
|
||||
ConstraintCategory::Predicate(_) | ConstraintCategory::OpaqueType => 4,
|
||||
// `Boring` constraints can correspond to user-written code and have useful spans,
|
||||
// but don't provide any other useful information for diagnostics.
|
||||
ConstraintCategory::Boring => 5,
|
||||
// `BoringNoLocation` constraints can point to user-written code, but are less
|
||||
// specific, and are not used for relations that would make sense to blame.
|
||||
ConstraintCategory::BoringNoLocation => 6,
|
||||
// Do not blame internal constraints.
|
||||
ConstraintCategory::Internal => 7,
|
||||
ConstraintCategory::IllegalUniverse => 8,
|
||||
}
|
||||
};
|
||||
|
||||
let best_choice =
|
||||
if blame_source { range.rev().find(find_region) } else { range.find(find_region) };
|
||||
let best_choice = if blame_source {
|
||||
path.iter().enumerate().rev().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0
|
||||
} else {
|
||||
path.iter().enumerate().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0
|
||||
};
|
||||
|
||||
debug!(?best_choice, ?blame_source, ?extra_info);
|
||||
debug!(?best_choice, ?blame_source);
|
||||
|
||||
if let Some(i) = best_choice {
|
||||
if let Some(next) = categorized_path.get(i + 1) {
|
||||
if matches!(categorized_path[i].category, ConstraintCategory::Return(_))
|
||||
&& next.category == ConstraintCategory::OpaqueType
|
||||
{
|
||||
// The return expression is being influenced by the return type being
|
||||
// impl Trait, point at the return type and not the return expr.
|
||||
return (next.clone(), extra_info);
|
||||
}
|
||||
let best_constraint = if let Some(next) = path.get(best_choice + 1)
|
||||
&& matches!(path[best_choice].category, ConstraintCategory::Return(_))
|
||||
&& next.category == ConstraintCategory::OpaqueType
|
||||
{
|
||||
// The return expression is being influenced by the return type being
|
||||
// impl Trait, point at the return type and not the return expr.
|
||||
*next
|
||||
} else if path[best_choice].category == ConstraintCategory::Return(ReturnConstraint::Normal)
|
||||
&& let Some(field) = path.iter().find_map(|p| {
|
||||
if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None }
|
||||
})
|
||||
{
|
||||
OutlivesConstraint {
|
||||
category: ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)),
|
||||
..path[best_choice]
|
||||
}
|
||||
} else {
|
||||
path[best_choice]
|
||||
};
|
||||
|
||||
if categorized_path[i].category == ConstraintCategory::Return(ReturnConstraint::Normal)
|
||||
{
|
||||
let field = categorized_path.iter().find_map(|p| {
|
||||
if let ConstraintCategory::ClosureUpvar(f) = p.category {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(field) = field {
|
||||
categorized_path[i].category =
|
||||
ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field));
|
||||
}
|
||||
}
|
||||
|
||||
return (categorized_path[i].clone(), extra_info);
|
||||
}
|
||||
|
||||
// If that search fails, that is.. unusual. Maybe everything
|
||||
// is in the same SCC or something. In that case, find what
|
||||
// appears to be the most interesting point to report to the
|
||||
// user via an even more ad-hoc guess.
|
||||
categorized_path.sort_by_key(|p| p.category);
|
||||
debug!("sorted_path={:#?}", categorized_path);
|
||||
|
||||
(categorized_path.remove(0), extra_info)
|
||||
let blame_constraint = BlameConstraint {
|
||||
category: best_constraint.category,
|
||||
from_closure: best_constraint.from_closure,
|
||||
cause: ObligationCause::new(best_constraint.span, CRATE_DEF_ID, cause_code.clone()),
|
||||
variance_info: best_constraint.variance_info,
|
||||
};
|
||||
(blame_constraint, path)
|
||||
}
|
||||
|
||||
pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
|
||||
|
|
|
|||
|
|
@ -544,12 +544,12 @@ fn pretty_print_region_elements(elements: impl IntoIterator<Item = RegionElement
|
|||
|
||||
return result;
|
||||
|
||||
fn push_location_range(str: &mut String, location1: Location, location2: Location) {
|
||||
fn push_location_range(s: &mut String, location1: Location, location2: Location) {
|
||||
if location1 == location2 {
|
||||
str.push_str(&format!("{location1:?}"));
|
||||
s.push_str(&format!("{location1:?}"));
|
||||
} else {
|
||||
assert_eq!(location1.block, location2.block);
|
||||
str.push_str(&format!(
|
||||
s.push_str(&format!(
|
||||
"{:?}[{}..={}]",
|
||||
location1.block, location1.statement_index, location2.statement_index
|
||||
));
|
||||
|
|
|
|||
|
|
@ -480,3 +480,10 @@ pub(crate) struct SimdIntrinsicArgConst {
|
|||
pub arg: usize,
|
||||
pub intrinsic: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(borrowck_tail_expr_drop_order)]
|
||||
pub(crate) struct TailExprDropOrder {
|
||||
#[label]
|
||||
pub borrowed: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
context.ambient_variance(),
|
||||
base_ty.ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::TypeAnnotation,
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::OpaqueCast),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
|
@ -333,7 +333,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
ty::Invariant,
|
||||
&UserTypeProjection { base: annotation_index, projs: vec![] },
|
||||
locations,
|
||||
ConstraintCategory::Boring,
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg),
|
||||
) {
|
||||
let annotation = &self.typeck.user_type_annotations[annotation_index];
|
||||
span_mirbug!(
|
||||
|
|
@ -455,7 +455,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
ty::Invariant,
|
||||
user_ty,
|
||||
Locations::All(*span),
|
||||
ConstraintCategory::TypeAnnotation,
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration),
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
@ -892,6 +892,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
Some(l) if !body.local_decls[l].is_user_variable() => {
|
||||
ConstraintCategory::Boring
|
||||
}
|
||||
Some(_)
|
||||
if let Some(body_id) = tcx
|
||||
.hir_node_by_def_id(body.source.def_id().expect_local())
|
||||
.body_id()
|
||||
&& let params = tcx.hir().body(body_id).params
|
||||
&& params
|
||||
.iter()
|
||||
.any(|param| param.span.contains(stmt.source_info.span)) =>
|
||||
{
|
||||
// Assignments generated from lowering argument patterns shouldn't be called
|
||||
// "assignments" in diagnostics and aren't interesting to blame for errors.
|
||||
ConstraintCategory::Boring
|
||||
}
|
||||
_ => ConstraintCategory::Assignment,
|
||||
};
|
||||
debug!(
|
||||
|
|
@ -927,7 +940,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
ty::Invariant,
|
||||
&UserTypeProjection { base: annotation_index, projs: vec![] },
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg),
|
||||
) {
|
||||
let annotation = &self.user_type_annotations[annotation_index];
|
||||
span_mirbug!(
|
||||
|
|
@ -962,7 +975,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
*variance,
|
||||
projection,
|
||||
Locations::All(stmt.source_info.span),
|
||||
ConstraintCategory::TypeAnnotation,
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::Ascription),
|
||||
) {
|
||||
let annotation = &self.user_type_annotations[projection.base];
|
||||
span_mirbug!(
|
||||
|
|
@ -1226,6 +1239,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
Some(l) if !body.local_decls[l].is_user_variable() => {
|
||||
ConstraintCategory::Boring
|
||||
}
|
||||
// The return type of a call is interesting for diagnostics.
|
||||
_ => ConstraintCategory::Assignment,
|
||||
};
|
||||
|
||||
|
|
@ -2169,7 +2183,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
ty_left,
|
||||
common_ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
ConstraintCategory::CallArgument(None),
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
bug!("Could not equate type variable with {:?}: {:?}", ty_left, err)
|
||||
|
|
@ -2178,7 +2192,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
ty_right,
|
||||
common_ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
ConstraintCategory::CallArgument(None),
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
use rustc_middle::mir::visit::{PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{Body, Local, Location};
|
||||
|
||||
pub(crate) trait FindAssignments {
|
||||
// Finds all statements that assign directly to local (i.e., X = ...)
|
||||
// and returns their locations.
|
||||
fn find_assignments(&self, local: Local) -> Vec<Location>;
|
||||
}
|
||||
|
||||
impl<'tcx> FindAssignments for Body<'tcx> {
|
||||
fn find_assignments(&self, local: Local) -> Vec<Location> {
|
||||
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
|
||||
visitor.visit_body(self);
|
||||
visitor.locations
|
||||
}
|
||||
}
|
||||
|
||||
// The Visitor walks the MIR to return the assignment statements corresponding
|
||||
// to a Local.
|
||||
struct FindLocalAssignmentVisitor {
|
||||
needle: Local,
|
||||
locations: Vec<Location>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
|
||||
fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
|
||||
if self.needle != local {
|
||||
return;
|
||||
}
|
||||
|
||||
if place_context.is_place_assignment() {
|
||||
self.locations.push(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
mod collect_writes;
|
||||
|
||||
pub(crate) use collect_writes::FindAssignments;
|
||||
|
|
@ -56,11 +56,6 @@ jobs:
|
|||
- os: macos-latest
|
||||
env:
|
||||
TARGET_TRIPLE: x86_64-apple-darwin
|
||||
# cross-compile from Linux to Windows using mingw
|
||||
- os: ubuntu-latest
|
||||
env:
|
||||
TARGET_TRIPLE: x86_64-pc-windows-gnu
|
||||
apt_deps: gcc-mingw-w64-x86-64 wine-stable
|
||||
- os: ubuntu-latest
|
||||
env:
|
||||
TARGET_TRIPLE: aarch64-unknown-linux-gnu
|
||||
|
|
@ -113,15 +108,6 @@ jobs:
|
|||
- name: Prepare dependencies
|
||||
run: ./y.sh prepare
|
||||
|
||||
# The Wine version shipped with Ubuntu 22.04 doesn't implement bcryptprimitives.dll
|
||||
- name: Build bcryptprimitives.dll shim for Wine
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
|
||||
run: |
|
||||
rustup target add x86_64-pc-windows-gnu
|
||||
mkdir wine_shims
|
||||
rustc patches/bcryptprimitives.rs -Copt-level=3 -Clto=fat --out-dir wine_shims --target x86_64-pc-windows-gnu
|
||||
echo "WINEPATH=$(pwd)/wine_shims" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: ./y.sh build --sysroot none
|
||||
|
||||
|
|
@ -135,9 +121,6 @@ jobs:
|
|||
|
||||
# This is roughly config rust-lang/rust uses for testing
|
||||
- name: Test with LLVM sysroot
|
||||
# Skip native x86_64-pc-windows-gnu. It is way too slow and cross-compiled
|
||||
# x86_64-pc-windows-gnu covers at least part of the tests.
|
||||
if: matrix.os != 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu'
|
||||
env:
|
||||
TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
|
||||
run: ./y.sh test --sysroot llvm --no-unstable-features
|
||||
|
|
@ -215,10 +198,6 @@ jobs:
|
|||
- os: macos-latest
|
||||
env:
|
||||
TARGET_TRIPLE: aarch64-apple-darwin
|
||||
# cross-compile from Linux to Windows using mingw
|
||||
- os: ubuntu-latest
|
||||
env:
|
||||
TARGET_TRIPLE: x86_64-pc-windows-gnu
|
||||
- os: windows-latest
|
||||
env:
|
||||
TARGET_TRIPLE: x86_64-pc-windows-msvc
|
||||
|
|
@ -243,12 +222,6 @@ jobs:
|
|||
if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin'
|
||||
run: rustup set default-host x86_64-apple-darwin
|
||||
|
||||
- name: Install MinGW toolchain
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-mingw-w64-x86-64
|
||||
|
||||
- name: Prepare dependencies
|
||||
run: ./y.sh prepare
|
||||
|
||||
|
|
@ -262,19 +235,11 @@ jobs:
|
|||
run: tar cvfJ cg_clif.tar.xz dist
|
||||
|
||||
- name: Upload prebuilt cg_clif
|
||||
if: matrix.os == 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cg_clif-${{ matrix.env.TARGET_TRIPLE }}
|
||||
path: cg_clif.tar.xz
|
||||
|
||||
- name: Upload prebuilt cg_clif (cross compile)
|
||||
if: matrix.os != 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cg_clif-${{ runner.os }}-cross-x86_64-mingw
|
||||
path: cg_clif.tar.xz
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
|
|
|||
|
|
@ -3,22 +3,16 @@
|
|||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.86"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
|
|
@ -37,6 +31,9 @@ name = "bumpalo"
|
|||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
|
@ -46,24 +43,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-bforest"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1"
|
||||
checksum = "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c"
|
||||
dependencies = [
|
||||
"cranelift-entity",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-bitset"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156"
|
||||
checksum = "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b"
|
||||
checksum = "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"cranelift-bforest",
|
||||
|
|
@ -74,7 +71,7 @@ dependencies = [
|
|||
"cranelift-entity",
|
||||
"cranelift-isle",
|
||||
"gimli",
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
"log",
|
||||
"regalloc2",
|
||||
"rustc-hash",
|
||||
|
|
@ -85,42 +82,42 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-meta"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7"
|
||||
checksum = "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a"
|
||||
dependencies = [
|
||||
"cranelift-codegen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-shared"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895"
|
||||
checksum = "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-control"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec"
|
||||
checksum = "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-entity"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96"
|
||||
checksum = "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84"
|
||||
dependencies = [
|
||||
"cranelift-bitset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-frontend"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e"
|
||||
checksum = "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"log",
|
||||
|
|
@ -130,15 +127,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-isle"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8"
|
||||
checksum = "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-jit"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62699329d4ced20fe281fbaef45e11b473b7ab310491b4bdebcd8b818a8ef7fe"
|
||||
checksum = "36972cab12ff246afe8d45b6a427669cf814bd393c661e5e8a8dedc26a81c73f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -156,9 +153,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-module"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f20b0b51ba962dac30fc7e812b86e4390d908acd4f59bcc8ac7610a8f3e0977"
|
||||
checksum = "11841b3f54ac480db1e8e8d5678ba901a13b387012d315e3f8fba3e7b7a80447"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -167,9 +164,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-native"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f"
|
||||
checksum = "7b7d8f895444fa52dd7bdd0bed11bf007a7fb43af65a6deac8fcc4094c6372f7"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"libc",
|
||||
|
|
@ -178,9 +175,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-object"
|
||||
version = "0.114.0"
|
||||
version = "0.115.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee231640a7ecceedd0f1f2782d9288db6a6908cc70675ed9427e3bf0ea6daacd"
|
||||
checksum = "8e235ddfd19f100855ad03358c7ae0a13070c38a000701054cab46458cca6e81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -212,6 +209,12 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
|
|
@ -228,18 +231,24 @@ name = "hashbrown"
|
|||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -281,27 +290,21 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.2"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.2",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
|
@ -317,14 +320,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regalloc2"
|
||||
version = "0.10.2"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0"
|
||||
checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"allocator-api2",
|
||||
"bumpalo",
|
||||
"hashbrown 0.15.2",
|
||||
"log",
|
||||
"rustc-hash",
|
||||
"slice-group-by",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
|
|
@ -366,30 +370,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slice-group-by"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
|
|
@ -404,9 +402,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.70"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -425,17 +423,11 @@ version = "1.0.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime-jit-icache-coherence"
|
||||
version = "27.0.0"
|
||||
version = "28.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad"
|
||||
checksum = "d40d7722b9e1fbeae135715710a8a2570b1e6cf72b74dd653962d89831c6c70d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
|
|
@ -524,23 +516,3 @@ name = "windows_x86_64_msvc"
|
|||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ crate-type = ["dylib"]
|
|||
|
||||
[dependencies]
|
||||
# These have to be in sync with each other
|
||||
cranelift-codegen = { version = "0.114.0", default-features = false, features = ["std", "unwind", "all-native-arch"] }
|
||||
cranelift-frontend = { version = "0.114.0" }
|
||||
cranelift-module = { version = "0.114.0" }
|
||||
cranelift-native = { version = "0.114.0" }
|
||||
cranelift-jit = { version = "0.114.0", optional = true }
|
||||
cranelift-object = { version = "0.114.0" }
|
||||
cranelift-codegen = { version = "0.115.0", default-features = false, features = ["std", "unwind", "all-native-arch"] }
|
||||
cranelift-frontend = { version = "0.115.0" }
|
||||
cranelift-module = { version = "0.115.0" }
|
||||
cranelift-native = { version = "0.115.0" }
|
||||
cranelift-jit = { version = "0.115.0", optional = true }
|
||||
cranelift-object = { version = "0.115.0" }
|
||||
target-lexicon = "0.12.0"
|
||||
gimli = { version = "0.31", default-features = false, features = ["write"] }
|
||||
object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
|
||||
|
|
|
|||
|
|
@ -16,11 +16,7 @@ static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github(
|
|||
"<none>",
|
||||
);
|
||||
|
||||
pub(crate) fn benchmark(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
|
||||
benchmark_simple_raytracer(dirs, bootstrap_host_compiler);
|
||||
}
|
||||
|
||||
fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
|
||||
pub(crate) fn benchmark(dirs: &Dirs, compiler: &Compiler) {
|
||||
if std::process::Command::new("hyperfine").output().is_err() {
|
||||
eprintln!("Hyperfine not installed");
|
||||
eprintln!("Hint: Try `cargo install hyperfine` to install hyperfine");
|
||||
|
|
@ -39,9 +35,9 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
|
|||
};
|
||||
|
||||
eprintln!("[BENCH COMPILE] ebobby/simple-raytracer");
|
||||
let cargo_clif = dirs
|
||||
.dist_dir
|
||||
.join(get_file_name(&bootstrap_host_compiler.rustc, "cargo_clif", "bin").replace('_', "-"));
|
||||
let cargo_clif = &compiler.cargo;
|
||||
let rustc_clif = &compiler.rustc;
|
||||
let rustflags = &compiler.rustflags.join("\x1f");
|
||||
let manifest_path = SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).join("Cargo.toml");
|
||||
let target_dir = dirs.build_dir.join("simple_raytracer");
|
||||
|
||||
|
|
@ -56,22 +52,24 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
|
|||
target_dir = target_dir.display(),
|
||||
);
|
||||
let clif_build_cmd = format!(
|
||||
"RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_clif || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_clif",
|
||||
"RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_clif || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_clif",
|
||||
cargo_clif = cargo_clif.display(),
|
||||
rustc_clif = rustc_clif.display(),
|
||||
manifest_path = manifest_path.display(),
|
||||
target_dir = target_dir.display(),
|
||||
);
|
||||
let clif_build_opt_cmd = format!(
|
||||
"RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm build/raytracer_cg_clif_opt || true) && ln build/simple_raytracer/release/main build/raytracer_cg_clif_opt",
|
||||
"RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm build/raytracer_cg_clif_opt || true) && ln build/simple_raytracer/release/main build/raytracer_cg_clif_opt",
|
||||
cargo_clif = cargo_clif.display(),
|
||||
rustc_clif = rustc_clif.display(),
|
||||
manifest_path = manifest_path.display(),
|
||||
target_dir = target_dir.display(),
|
||||
);
|
||||
|
||||
let bench_compile_markdown = dirs.dist_dir.join("bench_compile.md");
|
||||
let bench_compile_markdown = dirs.build_dir.join("bench_compile.md");
|
||||
|
||||
let bench_compile = hyperfine_command(
|
||||
1,
|
||||
0,
|
||||
bench_runs,
|
||||
Some(&clean_cmd),
|
||||
&[
|
||||
|
|
@ -92,23 +90,14 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
|
|||
|
||||
eprintln!("[BENCH RUN] ebobby/simple-raytracer");
|
||||
|
||||
let bench_run_markdown = dirs.dist_dir.join("bench_run.md");
|
||||
let bench_run_markdown = dirs.build_dir.join("bench_run.md");
|
||||
|
||||
let raytracer_cg_llvm = Path::new(".").join(get_file_name(
|
||||
&bootstrap_host_compiler.rustc,
|
||||
"raytracer_cg_llvm",
|
||||
"bin",
|
||||
));
|
||||
let raytracer_cg_clif = Path::new(".").join(get_file_name(
|
||||
&bootstrap_host_compiler.rustc,
|
||||
"raytracer_cg_clif",
|
||||
"bin",
|
||||
));
|
||||
let raytracer_cg_clif_opt = Path::new(".").join(get_file_name(
|
||||
&bootstrap_host_compiler.rustc,
|
||||
"raytracer_cg_clif_opt",
|
||||
"bin",
|
||||
));
|
||||
let raytracer_cg_llvm =
|
||||
Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_llvm", "bin"));
|
||||
let raytracer_cg_clif =
|
||||
Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_clif", "bin"));
|
||||
let raytracer_cg_clif_opt =
|
||||
Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_clif_opt", "bin"));
|
||||
let mut bench_run = hyperfine_command(
|
||||
0,
|
||||
bench_runs,
|
||||
|
|
|
|||
|
|
@ -33,23 +33,3 @@ pub(crate) fn get_bool(name: &str) -> bool {
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_value(name: &str) -> Option<String> {
|
||||
let values = load_config_file()
|
||||
.into_iter()
|
||||
.filter(|(key, _)| key == name)
|
||||
.map(|(_, val)| val)
|
||||
.collect::<Vec<_>>();
|
||||
if values.is_empty() {
|
||||
None
|
||||
} else if values.len() == 1 {
|
||||
if values[0].is_none() {
|
||||
eprintln!("Config `{}` missing value", name);
|
||||
process::exit(1);
|
||||
}
|
||||
values.into_iter().next().unwrap()
|
||||
} else {
|
||||
eprintln!("Config `{}` given multiple values: {:?}", name, values);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,10 +156,8 @@ fn main() {
|
|||
let cargo = rustc_info::get_cargo_path();
|
||||
let rustc = rustc_info::get_rustc_path();
|
||||
let rustdoc = rustc_info::get_rustdoc_path();
|
||||
let triple = std::env::var("HOST_TRIPLE")
|
||||
.ok()
|
||||
.or_else(|| config::get_value("host"))
|
||||
.unwrap_or_else(|| rustc_info::get_host_triple(&rustc));
|
||||
let triple =
|
||||
std::env::var("HOST_TRIPLE").unwrap_or_else(|_| rustc_info::get_host_triple(&rustc));
|
||||
Compiler {
|
||||
cargo,
|
||||
rustc,
|
||||
|
|
@ -170,10 +168,8 @@ fn main() {
|
|||
runner: vec![],
|
||||
}
|
||||
};
|
||||
let target_triple = std::env::var("TARGET_TRIPLE")
|
||||
.ok()
|
||||
.or_else(|| config::get_value("target"))
|
||||
.unwrap_or_else(|| bootstrap_host_compiler.triple.clone());
|
||||
let target_triple =
|
||||
std::env::var("TARGET_TRIPLE").unwrap_or_else(|_| bootstrap_host_compiler.triple.clone());
|
||||
|
||||
let dirs = path::Dirs {
|
||||
source_dir: current_dir.clone(),
|
||||
|
|
@ -247,7 +243,7 @@ fn main() {
|
|||
);
|
||||
}
|
||||
Command::Bench => {
|
||||
build_sysroot::build_sysroot(
|
||||
let compiler = build_sysroot::build_sysroot(
|
||||
&dirs,
|
||||
sysroot_kind,
|
||||
&cg_clif_dylib,
|
||||
|
|
@ -255,7 +251,7 @@ fn main() {
|
|||
rustup_toolchain_name.as_deref(),
|
||||
target_triple,
|
||||
);
|
||||
bench::benchmark(&dirs, &bootstrap_host_compiler);
|
||||
bench::benchmark(&dirs, &compiler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,20 +11,11 @@ pub(crate) struct Dirs {
|
|||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum PathBase {
|
||||
enum PathBase {
|
||||
Source,
|
||||
Build,
|
||||
}
|
||||
|
||||
impl PathBase {
|
||||
fn to_path(self, dirs: &Dirs) -> PathBuf {
|
||||
match self {
|
||||
PathBase::Source => dirs.source_dir.clone(),
|
||||
PathBase::Build => dirs.build_dir.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct RelPath {
|
||||
base: PathBase,
|
||||
|
|
@ -41,6 +32,9 @@ impl RelPath {
|
|||
}
|
||||
|
||||
pub(crate) fn to_path(&self, dirs: &Dirs) -> PathBuf {
|
||||
self.base.to_path(dirs).join(self.suffix)
|
||||
match self.base {
|
||||
PathBase::Source => dirs.source_dir.join(self.suffix),
|
||||
PathBase::Build => dirs.build_dir.join(self.suffix),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,5 @@
|
|||
# This file allows configuring the build system.
|
||||
|
||||
# Which triple to produce a compiler toolchain for.
|
||||
#
|
||||
# Defaults to the default triple of rustc on the host system.
|
||||
#host = x86_64-unknown-linux-gnu
|
||||
|
||||
# Which triple to build libraries (core/alloc/std/test/proc_macro) for.
|
||||
#
|
||||
# Defaults to `host`.
|
||||
#target = x86_64-unknown-linux-gnu
|
||||
|
||||
# Disables cleaning of the sysroot dir. This will cause old compiled artifacts to be re-used when
|
||||
# the sysroot source hasn't changed. This is useful when the codegen backend hasn't been modified.
|
||||
# This option can be changed while the build system is already running for as long as sysroot
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
// Shim for bcryptprimitives.dll. The Wine version shipped with Ubuntu 22.04
|
||||
// doesn't support it yet. Authored by @ChrisDenton
|
||||
|
||||
#![crate_type = "cdylib"]
|
||||
#![allow(nonstandard_style)]
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn ProcessPrng(mut pbData: *mut u8, mut cbData: usize) -> i32 {
|
||||
while cbData > 0 {
|
||||
let size = core::cmp::min(cbData, u32::MAX as usize);
|
||||
RtlGenRandom(pbData, size as u32);
|
||||
cbData -= size;
|
||||
pbData = pbData.add(size);
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
#[link(name = "advapi32")]
|
||||
extern "system" {
|
||||
#[link_name = "SystemFunction036"]
|
||||
pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: u32) -> u8;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2024-12-06"
|
||||
channel = "nightly-2025-01-05"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools"]
|
||||
profile = "minimal"
|
||||
|
|
|
|||
|
|
@ -123,12 +123,17 @@ rm tests/ui/mir/mir_raw_fat_ptr.rs # same
|
|||
rm tests/ui/consts/issue-33537.rs # same
|
||||
rm tests/ui/consts/const-mut-refs-crate.rs # same
|
||||
rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift
|
||||
rm tests/ui/invalid-compile-flags/crate-type-flag.rs # warning about proc-macros and panic=abort
|
||||
|
||||
# doesn't work due to the way the rustc test suite is invoked.
|
||||
# should work when using ./x.py test the way it is intended
|
||||
# ============================================================
|
||||
rm -r tests/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump
|
||||
rm -r tests/run-make/strip # same
|
||||
rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contains the standard library source
|
||||
rm -r tests/run-make/missing-unstable-trait-bound # This disables support for unstable features, but running cg_clif needs some unstable features
|
||||
rm -r tests/run-make/const-trait-stable-toolchain # same
|
||||
rm -r tests/run-make/incr-add-rust-src-component
|
||||
|
||||
# genuine bugs
|
||||
# ============
|
||||
|
|
@ -196,5 +201,5 @@ index e7ae773ffa1d3..04bc2d7787da7 100644
|
|||
EOF
|
||||
|
||||
echo "[TEST] rustc test suite"
|
||||
COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--nocapture tests/{codegen-units,run-make,ui,incremental}
|
||||
COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental}
|
||||
popd
|
||||
|
|
|
|||
|
|
@ -333,10 +333,9 @@ fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> {
|
|||
|
||||
let mut builder =
|
||||
ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap();
|
||||
// Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size
|
||||
// is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections
|
||||
// can easily double the amount of time necessary to perform linking.
|
||||
builder.per_function_section(sess.opts.unstable_opts.function_sections.unwrap_or(false));
|
||||
builder.per_function_section(
|
||||
sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections),
|
||||
);
|
||||
UnwindModule::new(ObjectModule::new(builder), true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ fn dep_symbol_lookup_fn(
|
|||
|
||||
let mut dylib_paths = Vec::new();
|
||||
|
||||
let data = &crate_info.dependency_formats[&rustc_session::config::CrateType::Executable].1;
|
||||
let data = &crate_info.dependency_formats[&rustc_session::config::CrateType::Executable];
|
||||
// `used_crates` is in reverse postorder in terms of dependencies. Reverse the order here to
|
||||
// get a postorder which ensures that all dependencies of a dylib are loaded before the dylib
|
||||
// itself. This helps the dynamic linker to find dylibs not in the regular dynamic library
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>(
|
|||
fx.bcx.ins().jump(destination_block, &[]);
|
||||
}
|
||||
None => {
|
||||
fx.bcx.ins().trap(TrapCode::user(0 /* unreachable */).unwrap());
|
||||
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1136,7 +1136,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
|||
_ => {
|
||||
fx.tcx.dcx().span_err(span, format!("Unknown SIMD intrinsic {}", intrinsic));
|
||||
// Prevent verifier error
|
||||
fx.bcx.ins().trap(TrapCode::user(0 /* unreachable */).unwrap());
|
||||
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ extern crate rustc_metadata;
|
|||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
extern crate rustc_target;
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
// This prevents duplicating functions and statics that are already part of the host rustc process.
|
||||
#[allow(unused_extern_crates)]
|
||||
|
|
@ -208,6 +210,7 @@ impl CodegenBackend for CraneliftCodegenBackend {
|
|||
need_metadata_module: bool,
|
||||
) -> Box<dyn Any> {
|
||||
tcx.dcx().abort_if_errors();
|
||||
info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE));
|
||||
let config = self.config.clone().unwrap_or_else(|| {
|
||||
BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args)
|
||||
.unwrap_or_else(|err| tcx.sess.dcx().fatal(err))
|
||||
|
|
|
|||
|
|
@ -660,9 +660,7 @@ pub unsafe fn optimize_thin_module(
|
|||
{
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
|
||||
if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) {
|
||||
return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
|
||||
}
|
||||
unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) };
|
||||
save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ pub(crate) struct UnstableCTargetFeature<'a> {
|
|||
#[diag(codegen_gcc_forbidden_ctarget_feature)]
|
||||
pub(crate) struct ForbiddenCTargetFeature<'a> {
|
||||
pub feature: &'a str,
|
||||
pub enabled: &'a str,
|
||||
pub reason: &'a str,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::iter::FromIterator;
|
||||
|
||||
#[cfg(feature = "master")]
|
||||
use gccjit::Context;
|
||||
use rustc_codegen_ssa::codegen_attrs::check_tied_features;
|
||||
use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::bug;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_session::Session;
|
||||
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
|
@ -37,82 +39,137 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
|
|||
let mut features = vec![];
|
||||
|
||||
// Features implied by an implicit or explicit `--target`.
|
||||
features.extend(
|
||||
sess.target
|
||||
.features
|
||||
.split(',')
|
||||
.filter(|v| !v.is_empty() && backend_feature_name(v).is_some())
|
||||
.map(String::from),
|
||||
);
|
||||
features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from));
|
||||
|
||||
// -Ctarget-features
|
||||
let known_features = sess.target.rust_target_features();
|
||||
let mut featsmap = FxHashMap::default();
|
||||
let feats = sess
|
||||
.opts
|
||||
.cg
|
||||
.target_feature
|
||||
.split(',')
|
||||
.filter_map(|s| {
|
||||
let enable_disable = match s.chars().next() {
|
||||
None => return None,
|
||||
Some(c @ ('+' | '-')) => c,
|
||||
Some(_) => {
|
||||
if diagnostics {
|
||||
sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s });
|
||||
}
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Get the backend feature name, if any.
|
||||
// This excludes rustc-specific features, that do not get passed down to GCC.
|
||||
let feature = backend_feature_name(s)?;
|
||||
// Warn against use of GCC specific feature names on the CLI.
|
||||
// Ensure that all ABI-required features are enabled, and the ABI-forbidden ones
|
||||
// are disabled.
|
||||
let abi_feature_constraints = sess.target.abi_required_features();
|
||||
let abi_incompatible_set =
|
||||
FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied());
|
||||
|
||||
// Compute implied features
|
||||
let mut all_rust_features = vec![];
|
||||
for feature in sess.opts.cg.target_feature.split(',') {
|
||||
if let Some(feature) = feature.strip_prefix('+') {
|
||||
all_rust_features.extend(
|
||||
UnordSet::from(sess.target.implied_target_features(std::iter::once(feature)))
|
||||
.to_sorted_stable_ord()
|
||||
.iter()
|
||||
.map(|&&s| (true, s)),
|
||||
)
|
||||
} else if let Some(feature) = feature.strip_prefix('-') {
|
||||
// FIXME: Why do we not remove implied features on "-" here?
|
||||
// We do the equivalent above in `target_features_cfg`.
|
||||
// See <https://github.com/rust-lang/rust/issues/134792>.
|
||||
all_rust_features.push((false, feature));
|
||||
} else if !feature.is_empty() {
|
||||
if diagnostics {
|
||||
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
|
||||
match feature_state {
|
||||
None => {
|
||||
let rust_feature =
|
||||
known_features.iter().find_map(|&(rust_feature, _, _)| {
|
||||
let gcc_features = to_gcc_features(sess, rust_feature);
|
||||
if gcc_features.contains(&feature)
|
||||
&& !gcc_features.contains(&rust_feature)
|
||||
{
|
||||
Some(rust_feature)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let unknown_feature = if let Some(rust_feature) = rust_feature {
|
||||
UnknownCTargetFeature {
|
||||
feature,
|
||||
rust_feature: PossibleFeature::Some { rust_feature },
|
||||
}
|
||||
} else {
|
||||
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
|
||||
};
|
||||
sess.dcx().emit_warn(unknown_feature);
|
||||
}
|
||||
Some((_, stability, _)) => {
|
||||
if let Err(reason) =
|
||||
stability.toggle_allowed(&sess.target, enable_disable == '+')
|
||||
sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove features that are meant for rustc, not codegen.
|
||||
all_rust_features.retain(|(_, feature)| {
|
||||
// Retain if it is not a rustc feature
|
||||
!RUSTC_SPECIFIC_FEATURES.contains(feature)
|
||||
});
|
||||
|
||||
// Check feature validity.
|
||||
if diagnostics {
|
||||
for &(enable, feature) in &all_rust_features {
|
||||
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
|
||||
match feature_state {
|
||||
None => {
|
||||
let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| {
|
||||
let gcc_features = to_gcc_features(sess, rust_feature);
|
||||
if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature)
|
||||
{
|
||||
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
|
||||
} else if stability.requires_nightly().is_some() {
|
||||
// An unstable feature. Warn about using it. (It makes little sense
|
||||
// to hard-error here since we just warn about fully unknown
|
||||
// features above).
|
||||
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
|
||||
Some(rust_feature)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let unknown_feature = if let Some(rust_feature) = rust_feature {
|
||||
UnknownCTargetFeature {
|
||||
feature,
|
||||
rust_feature: PossibleFeature::Some { rust_feature },
|
||||
}
|
||||
} else {
|
||||
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
|
||||
};
|
||||
sess.dcx().emit_warn(unknown_feature);
|
||||
}
|
||||
Some((_, stability, _)) => {
|
||||
if let Err(reason) = stability.toggle_allowed() {
|
||||
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||
feature,
|
||||
enabled: if enable { "enabled" } else { "disabled" },
|
||||
reason,
|
||||
});
|
||||
} else if stability.requires_nightly().is_some() {
|
||||
// An unstable feature. Warn about using it. (It makes little sense
|
||||
// to hard-error here since we just warn about fully unknown
|
||||
// features above).
|
||||
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(nagisa): figure out how to not allocate a full hashset here.
|
||||
featsmap.insert(feature, enable_disable == '+');
|
||||
}
|
||||
|
||||
// ... otherwise though we run through `to_gcc_features` when
|
||||
// Ensure that the features we enable/disable are compatible with the ABI.
|
||||
if enable {
|
||||
if abi_incompatible_set.contains(feature) {
|
||||
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||
feature,
|
||||
enabled: "enabled",
|
||||
reason: "this feature is incompatible with the target ABI",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// FIXME: we have to request implied features here since
|
||||
// negative features do not handle implied features above.
|
||||
for &required in abi_feature_constraints.required.iter() {
|
||||
let implied = sess.target.implied_target_features(std::iter::once(required));
|
||||
if implied.contains(feature) {
|
||||
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||
feature,
|
||||
enabled: "disabled",
|
||||
reason: "this feature is required by the target ABI",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(nagisa): figure out how to not allocate a full hashset here.
|
||||
featsmap.insert(feature, enable);
|
||||
}
|
||||
}
|
||||
|
||||
// To be sure the ABI-relevant features are all in the right state, we explicitly
|
||||
// (un)set them here. This means if the target spec sets those features wrong,
|
||||
// we will silently correct them rather than silently producing wrong code.
|
||||
// (The target sanity check tries to catch this, but we can't know which features are
|
||||
// enabled in GCC by default so we can't be fully sure about that check.)
|
||||
// We add these at the beginning of the list so that `-Ctarget-features` can
|
||||
// still override it... that's unsound, but more compatible with past behavior.
|
||||
all_rust_features.splice(
|
||||
0..0,
|
||||
abi_feature_constraints
|
||||
.required
|
||||
.iter()
|
||||
.map(|&f| (true, f))
|
||||
.chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))),
|
||||
);
|
||||
|
||||
// Translate this into GCC features.
|
||||
let feats = all_rust_features
|
||||
.iter()
|
||||
.filter_map(|&(enable, feature)| {
|
||||
let enable_disable = if enable { '+' } else { '-' };
|
||||
// We run through `to_gcc_features` when
|
||||
// passing requests down to GCC. This means that all in-language
|
||||
// features also work on the command line instead of having two
|
||||
// different names when the GCC name and the Rust name differ.
|
||||
|
|
@ -146,26 +203,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
|
|||
features
|
||||
}
|
||||
|
||||
/// Returns a feature name for the given `+feature` or `-feature` string.
|
||||
///
|
||||
/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
|
||||
fn backend_feature_name(s: &str) -> Option<&str> {
|
||||
// features must start with a `+` or `-`.
|
||||
let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| {
|
||||
bug!("target feature `{}` must begin with a `+` or `-`", s);
|
||||
});
|
||||
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
|
||||
// are not passed down to GCC.
|
||||
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
|
||||
return None;
|
||||
}
|
||||
Some(feature)
|
||||
}
|
||||
|
||||
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
|
||||
pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
|
||||
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
|
||||
match (arch, s) {
|
||||
// FIXME: seems like x87 does not exist?
|
||||
("x86", "x87") => smallvec![],
|
||||
("x86", "sse4.2") => smallvec!["sse4.2", "crc32"],
|
||||
("x86", "pclmulqdq") => smallvec!["pclmul"],
|
||||
("x86", "rdrand") => smallvec!["rdrnd"],
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ test = false
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
gimli = "0.30"
|
||||
itertools = "0.12"
|
||||
libc = "0.2"
|
||||
measureme = "11"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ codegen_llvm_dynamic_linking_with_lto =
|
|||
codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
|
||||
|
||||
codegen_llvm_forbidden_ctarget_feature =
|
||||
target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason}
|
||||
target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
|
||||
.note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
|
||||
|
||||
|
|
@ -24,8 +24,6 @@ codegen_llvm_invalid_minimum_alignment_not_power_of_two =
|
|||
codegen_llvm_invalid_minimum_alignment_too_large =
|
||||
invalid minimum global alignment: {$align} is too large
|
||||
|
||||
codegen_llvm_invalid_target_feature_prefix = target feature `{$feature}` must begin with a `+` or `-`"
|
||||
|
||||
codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}"
|
||||
codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err}
|
||||
|
||||
|
|
|
|||
|
|
@ -737,11 +737,7 @@ pub(crate) unsafe fn optimize_thin_module(
|
|||
{
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
|
||||
if unsafe {
|
||||
!llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target)
|
||||
} {
|
||||
return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
|
||||
}
|
||||
unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) };
|
||||
save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
|
||||
}
|
||||
|
||||
|
|
|
|||
37
compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs
Normal file
37
compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//! Definitions of various DWARF-related constants.
|
||||
|
||||
use libc::c_uint;
|
||||
|
||||
/// Helper macro to let us redeclare gimli's constants as our own constants
|
||||
/// with a different type, with less risk of copy-paste errors.
|
||||
macro_rules! declare_constant {
|
||||
(
|
||||
$name:ident : $type:ty
|
||||
) => {
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub(crate) const $name: $type = ::gimli::constants::$name.0 as $type;
|
||||
|
||||
// Assert that as-cast probably hasn't changed the value.
|
||||
const _: () = assert!($name as i128 == ::gimli::constants::$name.0 as i128);
|
||||
};
|
||||
}
|
||||
|
||||
declare_constant!(DW_TAG_const_type: c_uint);
|
||||
|
||||
// DWARF languages.
|
||||
declare_constant!(DW_LANG_Rust: c_uint);
|
||||
|
||||
// DWARF attribute type encodings.
|
||||
declare_constant!(DW_ATE_boolean: c_uint);
|
||||
declare_constant!(DW_ATE_float: c_uint);
|
||||
declare_constant!(DW_ATE_signed: c_uint);
|
||||
declare_constant!(DW_ATE_unsigned: c_uint);
|
||||
declare_constant!(DW_ATE_UTF: c_uint);
|
||||
|
||||
// DWARF expression operators.
|
||||
declare_constant!(DW_OP_deref: u64);
|
||||
declare_constant!(DW_OP_plus_uconst: u64);
|
||||
/// Defined by LLVM in `llvm/include/llvm/BinaryFormat/Dwarf.h`.
|
||||
/// Double-checked by a static assertion in `RustWrapper.cpp`.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000;
|
||||
|
|
@ -22,6 +22,7 @@ use rustc_target::spec::DebuginfoKind;
|
|||
use smallvec::smallvec;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
pub(crate) use self::type_map::TypeMap;
|
||||
use self::type_map::{DINodeCreationResult, Stub, UniqueTypeId};
|
||||
use super::CodegenUnitDebugContext;
|
||||
use super::namespace::mangled_name_of_instance;
|
||||
|
|
@ -30,6 +31,7 @@ use super::utils::{
|
|||
DIB, create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit,
|
||||
};
|
||||
use crate::common::{AsCCharPtr, CodegenCx};
|
||||
use crate::debuginfo::dwarf_const;
|
||||
use crate::debuginfo::metadata::type_map::build_type_with_children;
|
||||
use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind};
|
||||
use crate::llvm::debuginfo::{
|
||||
|
|
@ -59,23 +61,6 @@ impl fmt::Debug for llvm::Metadata {
|
|||
}
|
||||
}
|
||||
|
||||
// From DWARF 5.
|
||||
// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1.
|
||||
const DW_LANG_RUST: c_uint = 0x1c;
|
||||
#[allow(non_upper_case_globals)]
|
||||
const DW_ATE_boolean: c_uint = 0x02;
|
||||
#[allow(non_upper_case_globals)]
|
||||
const DW_ATE_float: c_uint = 0x04;
|
||||
#[allow(non_upper_case_globals)]
|
||||
const DW_ATE_signed: c_uint = 0x05;
|
||||
#[allow(non_upper_case_globals)]
|
||||
const DW_ATE_unsigned: c_uint = 0x07;
|
||||
#[allow(non_upper_case_globals)]
|
||||
const DW_ATE_UTF: c_uint = 0x10;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const DW_TAG_const_type: c_uint = 0x26;
|
||||
|
||||
pub(super) const UNKNOWN_LINE_NUMBER: c_uint = 0;
|
||||
pub(super) const UNKNOWN_COLUMN_NUMBER: c_uint = 0;
|
||||
|
||||
|
|
@ -90,8 +75,6 @@ type SmallVec<T> = smallvec::SmallVec<[T; 16]>;
|
|||
mod enums;
|
||||
mod type_map;
|
||||
|
||||
pub(crate) use type_map::TypeMap;
|
||||
|
||||
/// Returns from the enclosing function if the type debuginfo node with the given
|
||||
/// unique ID can be found in the type map.
|
||||
macro_rules! return_if_di_node_created_in_meantime {
|
||||
|
|
@ -522,7 +505,7 @@ fn recursion_marker_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll D
|
|||
name.as_c_char_ptr(),
|
||||
name.len(),
|
||||
cx.tcx.data_layout.pointer_size.bits(),
|
||||
DW_ATE_unsigned,
|
||||
dwarf_const::DW_ATE_unsigned,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
@ -781,6 +764,8 @@ fn build_basic_type_di_node<'ll, 'tcx>(
|
|||
// .natvis visualizers (and perhaps other existing native debuggers?)
|
||||
let cpp_like_debuginfo = cpp_like_debuginfo(cx.tcx);
|
||||
|
||||
use dwarf_const::{DW_ATE_UTF, DW_ATE_boolean, DW_ATE_float, DW_ATE_signed, DW_ATE_unsigned};
|
||||
|
||||
let (name, encoding) = match t.kind() {
|
||||
ty::Never => ("!", DW_ATE_unsigned),
|
||||
ty::Tuple(elements) if elements.is_empty() => {
|
||||
|
|
@ -961,7 +946,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
|
|||
|
||||
let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit(
|
||||
debug_context.builder,
|
||||
DW_LANG_RUST,
|
||||
dwarf_const::DW_LANG_Rust,
|
||||
compile_unit_file,
|
||||
producer.as_c_char_ptr(),
|
||||
producer.len(),
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty};
|
|||
use smallvec::smallvec;
|
||||
|
||||
use crate::common::{AsCCharPtr, CodegenCx};
|
||||
use crate::debuginfo::dwarf_const::DW_TAG_const_type;
|
||||
use crate::debuginfo::metadata::enums::DiscrResult;
|
||||
use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId};
|
||||
use crate::debuginfo::metadata::{
|
||||
DINodeCreationResult, DW_TAG_const_type, NO_GENERICS, NO_SCOPE_METADATA, SmallVec,
|
||||
UNKNOWN_LINE_NUMBER, build_field_di_node, file_metadata, file_metadata_from_def_id,
|
||||
size_and_align_of, type_di_node, unknown_file_metadata, visibility_di_flags,
|
||||
DINodeCreationResult, NO_GENERICS, NO_SCOPE_METADATA, SmallVec, UNKNOWN_LINE_NUMBER,
|
||||
build_field_di_node, file_metadata, file_metadata_from_def_id, size_and_align_of, type_di_node,
|
||||
unknown_file_metadata, visibility_di_flags,
|
||||
};
|
||||
use crate::debuginfo::utils::DIB;
|
||||
use crate::llvm::debuginfo::{DIFile, DIFlags, DIType};
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ use crate::llvm::debuginfo::{
|
|||
use crate::value::Value;
|
||||
|
||||
mod create_scope_map;
|
||||
mod dwarf_const;
|
||||
mod gdb;
|
||||
pub(crate) mod metadata;
|
||||
mod namespace;
|
||||
|
|
@ -47,6 +48,10 @@ mod utils;
|
|||
use self::create_scope_map::compute_mir_scopes;
|
||||
pub(crate) use self::metadata::build_global_var_di_node;
|
||||
|
||||
// FIXME(Zalathar): These `DW_TAG_*` constants are fake values that were
|
||||
// removed from LLVM in 2015, and are only used by our own `RustWrapper.cpp`
|
||||
// to decide which C++ API to call. Instead, we should just have two separate
|
||||
// FFI functions and choose the correct one on the Rust side.
|
||||
#[allow(non_upper_case_globals)]
|
||||
const DW_TAG_auto_variable: c_uint = 0x100;
|
||||
#[allow(non_upper_case_globals)]
|
||||
|
|
@ -152,29 +157,26 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
|
|||
indirect_offsets: &[Size],
|
||||
fragment: Option<Range<Size>>,
|
||||
) {
|
||||
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst};
|
||||
|
||||
// Convert the direct and indirect offsets and fragment byte range to address ops.
|
||||
// FIXME(eddyb) use `const`s instead of getting the values via FFI,
|
||||
// the values should match the ones in the DWARF standard anyway.
|
||||
let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() };
|
||||
let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() };
|
||||
let op_llvm_fragment = || unsafe { llvm::LLVMRustDIBuilderCreateOpLLVMFragment() };
|
||||
let mut addr_ops = SmallVec::<[u64; 8]>::new();
|
||||
|
||||
if direct_offset.bytes() > 0 {
|
||||
addr_ops.push(op_plus_uconst());
|
||||
addr_ops.push(DW_OP_plus_uconst);
|
||||
addr_ops.push(direct_offset.bytes() as u64);
|
||||
}
|
||||
for &offset in indirect_offsets {
|
||||
addr_ops.push(op_deref());
|
||||
addr_ops.push(DW_OP_deref);
|
||||
if offset.bytes() > 0 {
|
||||
addr_ops.push(op_plus_uconst());
|
||||
addr_ops.push(DW_OP_plus_uconst);
|
||||
addr_ops.push(offset.bytes() as u64);
|
||||
}
|
||||
}
|
||||
if let Some(fragment) = fragment {
|
||||
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
|
||||
// offset and size, both of them in bits.
|
||||
addr_ops.push(op_llvm_fragment());
|
||||
addr_ops.push(DW_OP_LLVM_fragment);
|
||||
addr_ops.push(fragment.start.bits() as u64);
|
||||
addr_ops.push((fragment.end - fragment.start).bits() as u64);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ pub(crate) struct UnstableCTargetFeature<'a> {
|
|||
#[note(codegen_llvm_forbidden_ctarget_feature_issue)]
|
||||
pub(crate) struct ForbiddenCTargetFeature<'a> {
|
||||
pub feature: &'a str,
|
||||
pub enabled: &'a str,
|
||||
pub reason: &'a str,
|
||||
}
|
||||
|
||||
|
|
@ -213,12 +214,6 @@ pub(crate) struct MismatchedDataLayout<'a> {
|
|||
pub llvm_layout: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_invalid_target_feature_prefix)]
|
||||
pub(crate) struct InvalidTargetFeaturePrefix<'a> {
|
||||
pub feature: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_fixed_x18_invalid_arch)]
|
||||
pub(crate) struct FixedX18InvalidArch<'a> {
|
||||
|
|
|
|||
|
|
@ -2177,9 +2177,6 @@ unsafe extern "C" {
|
|||
Location: &'a DILocation,
|
||||
BD: c_uint,
|
||||
) -> Option<&'a DILocation>;
|
||||
pub fn LLVMRustDIBuilderCreateOpDeref() -> u64;
|
||||
pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64;
|
||||
pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64;
|
||||
|
||||
pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);
|
||||
pub fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString);
|
||||
|
|
@ -2377,7 +2374,7 @@ unsafe extern "C" {
|
|||
Data: &ThinLTOData,
|
||||
Module: &Module,
|
||||
Target: &TargetMachine,
|
||||
) -> bool;
|
||||
);
|
||||
pub fn LLVMRustPrepareThinLTOResolveWeak(Data: &ThinLTOData, Module: &Module) -> bool;
|
||||
pub fn LLVMRustPrepareThinLTOInternalize(Data: &ThinLTOData, Module: &Module) -> bool;
|
||||
pub fn LLVMRustPrepareThinLTOImport(
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATU
|
|||
|
||||
use crate::back::write::create_informational_target_machine;
|
||||
use crate::errors::{
|
||||
FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature,
|
||||
UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
|
||||
FixedX18InvalidArch, ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature,
|
||||
UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
|
||||
};
|
||||
use crate::llvm;
|
||||
|
||||
|
|
@ -109,7 +109,10 @@ unsafe fn configure_llvm(sess: &Session) {
|
|||
add("-wasm-enable-eh", false);
|
||||
}
|
||||
|
||||
if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind {
|
||||
if sess.target.os == "emscripten"
|
||||
&& !sess.opts.unstable_opts.emscripten_wasm_eh
|
||||
&& sess.panic_strategy() == PanicStrategy::Unwind
|
||||
{
|
||||
add("-enable-emscripten-cxx-exceptions", false);
|
||||
}
|
||||
|
||||
|
|
@ -348,7 +351,16 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol>
|
|||
{
|
||||
if enabled {
|
||||
// Also add all transitively implied features.
|
||||
features.extend(sess.target.implied_target_features(std::iter::once(feature)));
|
||||
|
||||
// We don't care about the order in `features` since the only thing we use it for is the
|
||||
// `features.contains` below.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
features.extend(
|
||||
sess.target
|
||||
.implied_target_features(std::iter::once(feature.as_str()))
|
||||
.iter()
|
||||
.map(|s| Symbol::intern(s)),
|
||||
);
|
||||
} else {
|
||||
// Remove transitively reverse-implied features.
|
||||
|
||||
|
|
@ -356,7 +368,11 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol>
|
|||
// `features.contains` below.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
features.retain(|f| {
|
||||
if sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) {
|
||||
if sess
|
||||
.target
|
||||
.implied_target_features(std::iter::once(f.as_str()))
|
||||
.contains(&feature.as_str())
|
||||
{
|
||||
// If `f` if implies `feature`, then `!feature` implies `!f`, so we have to
|
||||
// remove `f`. (This is the standard logical contraposition principle.)
|
||||
false
|
||||
|
|
@ -638,7 +654,7 @@ pub(crate) fn global_llvm_features(
|
|||
sess.target
|
||||
.features
|
||||
.split(',')
|
||||
.filter(|v| !v.is_empty() && backend_feature_name(sess, v).is_some())
|
||||
.filter(|v| !v.is_empty())
|
||||
// Drop +v8plus feature introduced in LLVM 20.
|
||||
.filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0))
|
||||
.map(String::from),
|
||||
|
|
@ -651,89 +667,136 @@ pub(crate) fn global_llvm_features(
|
|||
// -Ctarget-features
|
||||
if !only_base_features {
|
||||
let known_features = sess.target.rust_target_features();
|
||||
// Will only be filled when `diagnostics` is set!
|
||||
let mut featsmap = FxHashMap::default();
|
||||
|
||||
// insert implied features
|
||||
// Ensure that all ABI-required features are enabled, and the ABI-forbidden ones
|
||||
// are disabled.
|
||||
let abi_feature_constraints = sess.target.abi_required_features();
|
||||
let abi_incompatible_set =
|
||||
FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied());
|
||||
|
||||
// Compute implied features
|
||||
let mut all_rust_features = vec![];
|
||||
for feature in sess.opts.cg.target_feature.split(',') {
|
||||
match feature.strip_prefix('+') {
|
||||
Some(feature) => all_rust_features.extend(
|
||||
UnordSet::from(
|
||||
sess.target
|
||||
.implied_target_features(std::iter::once(Symbol::intern(feature))),
|
||||
)
|
||||
.to_sorted_stable_ord()
|
||||
.iter()
|
||||
.map(|s| format!("+{}", s.as_str())),
|
||||
),
|
||||
_ => all_rust_features.push(feature.to_string()),
|
||||
if let Some(feature) = feature.strip_prefix('+') {
|
||||
all_rust_features.extend(
|
||||
UnordSet::from(sess.target.implied_target_features(std::iter::once(feature)))
|
||||
.to_sorted_stable_ord()
|
||||
.iter()
|
||||
.map(|&&s| (true, s)),
|
||||
)
|
||||
} else if let Some(feature) = feature.strip_prefix('-') {
|
||||
// FIXME: Why do we not remove implied features on "-" here?
|
||||
// We do the equivalent above in `target_features_cfg`.
|
||||
// See <https://github.com/rust-lang/rust/issues/134792>.
|
||||
all_rust_features.push((false, feature));
|
||||
} else if !feature.is_empty() {
|
||||
if diagnostics {
|
||||
sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove features that are meant for rustc, not LLVM.
|
||||
all_rust_features.retain(|(_, feature)| {
|
||||
// Retain if it is not a rustc feature
|
||||
!RUSTC_SPECIFIC_FEATURES.contains(feature)
|
||||
});
|
||||
|
||||
// Check feature validity.
|
||||
if diagnostics {
|
||||
for &(enable, feature) in &all_rust_features {
|
||||
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
|
||||
match feature_state {
|
||||
None => {
|
||||
let rust_feature =
|
||||
known_features.iter().find_map(|&(rust_feature, _, _)| {
|
||||
let llvm_features = to_llvm_features(sess, rust_feature)?;
|
||||
if llvm_features.contains(feature)
|
||||
&& !llvm_features.contains(rust_feature)
|
||||
{
|
||||
Some(rust_feature)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let unknown_feature = if let Some(rust_feature) = rust_feature {
|
||||
UnknownCTargetFeature {
|
||||
feature,
|
||||
rust_feature: PossibleFeature::Some { rust_feature },
|
||||
}
|
||||
} else {
|
||||
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
|
||||
};
|
||||
sess.dcx().emit_warn(unknown_feature);
|
||||
}
|
||||
Some((_, stability, _)) => {
|
||||
if let Err(reason) = stability.toggle_allowed() {
|
||||
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||
feature,
|
||||
enabled: if enable { "enabled" } else { "disabled" },
|
||||
reason,
|
||||
});
|
||||
} else if stability.requires_nightly().is_some() {
|
||||
// An unstable feature. Warn about using it. It makes little sense
|
||||
// to hard-error here since we just warn about fully unknown
|
||||
// features above.
|
||||
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the features we enable/disable are compatible with the ABI.
|
||||
if enable {
|
||||
if abi_incompatible_set.contains(feature) {
|
||||
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||
feature,
|
||||
enabled: "enabled",
|
||||
reason: "this feature is incompatible with the target ABI",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// FIXME: we have to request implied features here since
|
||||
// negative features do not handle implied features above.
|
||||
for &required in abi_feature_constraints.required.iter() {
|
||||
let implied =
|
||||
sess.target.implied_target_features(std::iter::once(required));
|
||||
if implied.contains(feature) {
|
||||
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||
feature,
|
||||
enabled: "disabled",
|
||||
reason: "this feature is required by the target ABI",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(nagisa): figure out how to not allocate a full hashset here.
|
||||
featsmap.insert(feature, enable);
|
||||
}
|
||||
}
|
||||
|
||||
// To be sure the ABI-relevant features are all in the right state, we explicitly
|
||||
// (un)set them here. This means if the target spec sets those features wrong,
|
||||
// we will silently correct them rather than silently producing wrong code.
|
||||
// (The target sanity check tries to catch this, but we can't know which features are
|
||||
// enabled in LLVM by default so we can't be fully sure about that check.)
|
||||
// We add these at the beginning of the list so that `-Ctarget-features` can
|
||||
// still override it... that's unsound, but more compatible with past behavior.
|
||||
all_rust_features.splice(
|
||||
0..0,
|
||||
abi_feature_constraints
|
||||
.required
|
||||
.iter()
|
||||
.map(|&f| (true, f))
|
||||
.chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))),
|
||||
);
|
||||
|
||||
// Translate this into LLVM features.
|
||||
let feats = all_rust_features
|
||||
.iter()
|
||||
.filter_map(|s| {
|
||||
let enable_disable = match s.chars().next() {
|
||||
None => return None,
|
||||
Some(c @ ('+' | '-')) => c,
|
||||
Some(_) => {
|
||||
if diagnostics {
|
||||
sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s });
|
||||
}
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Get the backend feature name, if any.
|
||||
// This excludes rustc-specific features, which do not get passed to LLVM.
|
||||
let feature = backend_feature_name(sess, s)?;
|
||||
// Warn against use of LLVM specific feature names and unstable features on the CLI.
|
||||
if diagnostics {
|
||||
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
|
||||
match feature_state {
|
||||
None => {
|
||||
let rust_feature =
|
||||
known_features.iter().find_map(|&(rust_feature, _, _)| {
|
||||
let llvm_features = to_llvm_features(sess, rust_feature)?;
|
||||
if llvm_features.contains(feature)
|
||||
&& !llvm_features.contains(rust_feature)
|
||||
{
|
||||
Some(rust_feature)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let unknown_feature = if let Some(rust_feature) = rust_feature {
|
||||
UnknownCTargetFeature {
|
||||
feature,
|
||||
rust_feature: PossibleFeature::Some { rust_feature },
|
||||
}
|
||||
} else {
|
||||
UnknownCTargetFeature {
|
||||
feature,
|
||||
rust_feature: PossibleFeature::None,
|
||||
}
|
||||
};
|
||||
sess.dcx().emit_warn(unknown_feature);
|
||||
}
|
||||
Some((_, stability, _)) => {
|
||||
if let Err(reason) =
|
||||
stability.toggle_allowed(&sess.target, enable_disable == '+')
|
||||
{
|
||||
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
|
||||
} else if stability.requires_nightly().is_some() {
|
||||
// An unstable feature. Warn about using it. It makes little sense
|
||||
// to hard-error here since we just warn about fully unknown
|
||||
// features above.
|
||||
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(nagisa): figure out how to not allocate a full hashset here.
|
||||
featsmap.insert(feature, enable_disable == '+');
|
||||
}
|
||||
|
||||
.filter_map(|&(enable, feature)| {
|
||||
let enable_disable = if enable { '+' } else { '-' };
|
||||
// We run through `to_llvm_features` when
|
||||
// passing requests down to LLVM. This means that all in-language
|
||||
// features also work on the command line instead of having two
|
||||
|
|
@ -746,9 +809,9 @@ pub(crate) fn global_llvm_features(
|
|||
enable_disable, llvm_feature.llvm_feature_name
|
||||
))
|
||||
.chain(llvm_feature.dependency.into_iter().filter_map(
|
||||
move |feat| match (enable_disable, feat) {
|
||||
('-' | '+', TargetFeatureFoldStrength::Both(f))
|
||||
| ('+', TargetFeatureFoldStrength::EnableOnly(f)) => {
|
||||
move |feat| match (enable, feat) {
|
||||
(_, TargetFeatureFoldStrength::Both(f))
|
||||
| (true, TargetFeatureFoldStrength::EnableOnly(f)) => {
|
||||
Some(format!("{enable_disable}{f}"))
|
||||
}
|
||||
_ => None,
|
||||
|
|
@ -780,22 +843,6 @@ pub(crate) fn global_llvm_features(
|
|||
features
|
||||
}
|
||||
|
||||
/// Returns a feature name for the given `+feature` or `-feature` string.
|
||||
///
|
||||
/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
|
||||
fn backend_feature_name<'a>(sess: &Session, s: &'a str) -> Option<&'a str> {
|
||||
// features must start with a `+` or `-`.
|
||||
let feature = s
|
||||
.strip_prefix(&['+', '-'][..])
|
||||
.unwrap_or_else(|| sess.dcx().emit_fatal(InvalidTargetFeaturePrefix { feature: s }));
|
||||
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
|
||||
// are not passed down to LLVM.
|
||||
if s.is_empty() || RUSTC_SPECIFIC_FEATURES.contains(&feature) {
|
||||
return None;
|
||||
}
|
||||
Some(feature)
|
||||
}
|
||||
|
||||
pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> {
|
||||
let name = sess.opts.unstable_opts.tune_cpu.as_ref()?;
|
||||
Some(handle_native(name))
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error}
|
|||
codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
|
||||
|
||||
codegen_ssa_forbidden_target_feature_attr =
|
||||
target feature `{$feature}` cannot be toggled with `#[target_feature]`: {$reason}
|
||||
target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason}
|
||||
|
||||
codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced
|
||||
|
||||
|
|
|
|||
|
|
@ -2451,10 +2451,12 @@ fn add_order_independent_options(
|
|||
}
|
||||
|
||||
if sess.target.os == "emscripten" {
|
||||
cmd.cc_arg("-s").cc_arg(if sess.panic_strategy() == PanicStrategy::Abort {
|
||||
"DISABLE_EXCEPTION_CATCHING=1"
|
||||
cmd.cc_arg(if sess.panic_strategy() == PanicStrategy::Abort {
|
||||
"-sDISABLE_EXCEPTION_CATCHING=1"
|
||||
} else if sess.opts.unstable_opts.emscripten_wasm_eh {
|
||||
"-fwasm-exceptions"
|
||||
} else {
|
||||
"DISABLE_EXCEPTION_CATCHING=0"
|
||||
"-sDISABLE_EXCEPTION_CATCHING=0"
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -388,7 +388,8 @@ pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
// exceptions. This means that the VM does the unwinding for
|
||||
// us
|
||||
pub fn wants_wasm_eh(sess: &Session) -> bool {
|
||||
sess.target.is_like_wasm && sess.target.os != "emscripten"
|
||||
sess.target.is_like_wasm
|
||||
&& (sess.target.os != "emscripten" || sess.opts.unstable_opts.emscripten_wasm_eh)
|
||||
}
|
||||
|
||||
/// Returns `true` if this session's target will use SEH-based unwinding.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use crate::errors;
|
|||
pub(crate) fn from_target_feature_attr(
|
||||
tcx: TyCtxt<'_>,
|
||||
attr: &hir::Attribute,
|
||||
rust_target_features: &UnordMap<String, target_features::StabilityComputed>,
|
||||
rust_target_features: &UnordMap<String, target_features::Stability>,
|
||||
target_features: &mut Vec<TargetFeature>,
|
||||
) {
|
||||
let Some(list) = attr.meta_item_list() else { return };
|
||||
|
|
@ -32,7 +32,7 @@ pub(crate) fn from_target_feature_attr(
|
|||
.emit();
|
||||
};
|
||||
let rust_features = tcx.features();
|
||||
let mut added_target_features = Vec::new();
|
||||
let abi_feature_constraints = tcx.sess.target.abi_required_features();
|
||||
for item in list {
|
||||
// Only `enable = ...` is accepted in the meta-item list.
|
||||
if !item.has_name(sym::enable) {
|
||||
|
|
@ -47,7 +47,7 @@ pub(crate) fn from_target_feature_attr(
|
|||
};
|
||||
|
||||
// We allow comma separation to enable multiple features.
|
||||
added_target_features.extend(value.as_str().split(',').filter_map(|feature| {
|
||||
for feature in value.as_str().split(',') {
|
||||
let Some(stability) = rust_target_features.get(feature) else {
|
||||
let msg = format!("the feature named `{feature}` is not valid for this target");
|
||||
let mut err = tcx.dcx().struct_span_err(item.span(), msg);
|
||||
|
|
@ -59,12 +59,12 @@ pub(crate) fn from_target_feature_attr(
|
|||
}
|
||||
}
|
||||
err.emit();
|
||||
return None;
|
||||
continue;
|
||||
};
|
||||
|
||||
// Only allow target features whose feature gates have been enabled
|
||||
// and which are permitted to be toggled.
|
||||
if let Err(reason) = stability.toggle_allowed(/*enable*/ true) {
|
||||
if let Err(reason) = stability.toggle_allowed() {
|
||||
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
|
||||
span: item.span(),
|
||||
feature,
|
||||
|
|
@ -80,31 +80,25 @@ pub(crate) fn from_target_feature_attr(
|
|||
format!("the target feature `{feature}` is currently unstable"),
|
||||
)
|
||||
.emit();
|
||||
} else {
|
||||
// Add this and the implied features.
|
||||
let feature_sym = Symbol::intern(feature);
|
||||
for &name in tcx.implied_target_features(feature_sym) {
|
||||
// But ensure the ABI does not forbid enabling this.
|
||||
// Here we do assume that LLVM doesn't add even more implied features
|
||||
// we don't know about, at least no features that would have ABI effects!
|
||||
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
|
||||
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
|
||||
span: item.span(),
|
||||
feature: name.as_str(),
|
||||
reason: "this feature is incompatible with the target ABI",
|
||||
});
|
||||
}
|
||||
target_features.push(TargetFeature { name, implied: name != feature_sym })
|
||||
}
|
||||
}
|
||||
Some(Symbol::intern(feature))
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Add explicit features
|
||||
target_features.extend(
|
||||
added_target_features.iter().copied().map(|name| TargetFeature { name, implied: false }),
|
||||
);
|
||||
|
||||
// Add implied features
|
||||
let mut implied_target_features = UnordSet::new();
|
||||
for feature in added_target_features.iter() {
|
||||
implied_target_features.extend(tcx.implied_target_features(*feature).clone());
|
||||
}
|
||||
for feature in added_target_features.iter() {
|
||||
implied_target_features.remove(feature);
|
||||
}
|
||||
target_features.extend(
|
||||
implied_target_features
|
||||
.into_sorted_stable_ord()
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|name| TargetFeature { name, implied: true }),
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the set of target features used in a function for the purposes of
|
||||
|
|
@ -147,25 +141,28 @@ pub(crate) fn provide(providers: &mut Providers) {
|
|||
*providers = Providers {
|
||||
rust_target_features: |tcx, cnum| {
|
||||
assert_eq!(cnum, LOCAL_CRATE);
|
||||
let target = &tcx.sess.target;
|
||||
if tcx.sess.opts.actually_rustdoc {
|
||||
// rustdoc needs to be able to document functions that use all the features, so
|
||||
// whitelist them all
|
||||
rustc_target::target_features::all_rust_features()
|
||||
.map(|(a, b)| (a.to_string(), b.compute_toggleability(target)))
|
||||
.map(|(a, b)| (a.to_string(), b))
|
||||
.collect()
|
||||
} else {
|
||||
tcx.sess
|
||||
.target
|
||||
.rust_target_features()
|
||||
.iter()
|
||||
.map(|(a, b, _)| (a.to_string(), b.compute_toggleability(target)))
|
||||
.map(|(a, b, _)| (a.to_string(), *b))
|
||||
.collect()
|
||||
}
|
||||
},
|
||||
implied_target_features: |tcx, feature| {
|
||||
implied_target_features: |tcx, feature: Symbol| {
|
||||
let feature = feature.as_str();
|
||||
UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature)))
|
||||
.into_sorted_stable_ord()
|
||||
.into_iter()
|
||||
.map(|s| Symbol::intern(s))
|
||||
.collect()
|
||||
},
|
||||
asm_target_features,
|
||||
..*providers
|
||||
|
|
|
|||
|
|
@ -704,8 +704,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> {
|
||||
let len = mplace.len(self)?;
|
||||
let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len))?;
|
||||
let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
|
||||
interp_ok(str)
|
||||
let s = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
|
||||
interp_ok(s)
|
||||
}
|
||||
|
||||
/// Read from a local of the current frame. Convenience method for [`InterpCx::local_at_frame_to_op`].
|
||||
|
|
|
|||
|
|
@ -1017,9 +1017,9 @@ where
|
|||
/// This is allocated in immutable global memory and deduplicated.
|
||||
pub fn allocate_str_dedup(
|
||||
&mut self,
|
||||
str: &str,
|
||||
s: &str,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
let bytes = str.as_bytes();
|
||||
let bytes = s.as_bytes();
|
||||
let ptr = self.allocate_bytes_dedup(bytes)?;
|
||||
|
||||
// Create length metadata for the string.
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ use rustc_metadata::locator;
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
|
||||
use rustc_session::config::{
|
||||
CG_OPTIONS, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, Z_OPTIONS,
|
||||
nightly_options,
|
||||
CG_OPTIONS, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, UnstableOptions,
|
||||
Z_OPTIONS, nightly_options,
|
||||
};
|
||||
use rustc_session::getopts::{self, Matches};
|
||||
use rustc_session::lint::{Lint, LintId};
|
||||
|
|
@ -1124,14 +1124,6 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) ->
|
|||
return true;
|
||||
}
|
||||
|
||||
if cg_flags.iter().any(|x| *x == "no-stack-check") {
|
||||
early_dcx.early_warn("the `-Cno-stack-check` flag is deprecated and does nothing");
|
||||
}
|
||||
|
||||
if cg_flags.iter().any(|x| x.starts_with("inline-threshold")) {
|
||||
early_dcx.early_warn("the `-Cinline-threshold` flag is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`)");
|
||||
}
|
||||
|
||||
if cg_flags.iter().any(|x| *x == "passes=list") {
|
||||
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
|
||||
|
||||
|
|
@ -1156,18 +1148,16 @@ fn describe_codegen_flags() {
|
|||
print_flag_list("-C", config::CG_OPTIONS);
|
||||
}
|
||||
|
||||
fn print_flag_list<T>(
|
||||
cmdline_opt: &str,
|
||||
flag_list: &[(&'static str, T, &'static str, &'static str)],
|
||||
) {
|
||||
let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
|
||||
fn print_flag_list<T>(cmdline_opt: &str, flag_list: &[OptionDesc<T>]) {
|
||||
let max_len =
|
||||
flag_list.iter().map(|opt_desc| opt_desc.name().chars().count()).max().unwrap_or(0);
|
||||
|
||||
for &(name, _, _, desc) in flag_list {
|
||||
for opt_desc in flag_list {
|
||||
safe_println!(
|
||||
" {} {:>width$}=val -- {}",
|
||||
cmdline_opt,
|
||||
name.replace('_', "-"),
|
||||
desc,
|
||||
opt_desc.name().replace('_', "-"),
|
||||
opt_desc.desc(),
|
||||
width = max_len
|
||||
);
|
||||
}
|
||||
|
|
@ -1221,8 +1211,8 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
|
|||
let msg: Option<String> = match e {
|
||||
getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS
|
||||
.iter()
|
||||
.map(|&(name, ..)| ('C', name))
|
||||
.chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name)))
|
||||
.map(|opt_desc| ('C', opt_desc.name()))
|
||||
.chain(Z_OPTIONS.iter().map(|opt_desc| ('Z', opt_desc.name())))
|
||||
.find(|&(_, name)| *opt == name.replace('_', "-"))
|
||||
.map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")),
|
||||
getopts::Fail::ArgumentMissing(ref opt) => {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ const GATED_CFGS: &[GatedCfg] = &[
|
|||
(sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
|
||||
// this is consistent with naming of the compiler flag it's for
|
||||
(sym::fmt_debug, sym::fmt_debug, Features::fmt_debug),
|
||||
(sym::emscripten_wasm_eh, sym::cfg_emscripten_wasm_eh, Features::cfg_emscripten_wasm_eh),
|
||||
];
|
||||
|
||||
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
|
||||
|
|
|
|||
|
|
@ -202,6 +202,8 @@ declare_features! (
|
|||
(internal, allow_internal_unstable, "1.0.0", None),
|
||||
/// Allows using anonymous lifetimes in argument-position impl-trait.
|
||||
(unstable, anonymous_lifetime_in_impl_trait, "1.63.0", None),
|
||||
/// Allows access to the emscripten_wasm_eh config, used by panic_unwind and unwind
|
||||
(internal, cfg_emscripten_wasm_eh, "CURRENT_RUSTC_VERSION", None),
|
||||
/// Allows identifying the `compiler_builtins` crate.
|
||||
(internal, compiler_builtins, "1.13.0", None),
|
||||
/// Allows writing custom MIR
|
||||
|
|
|
|||
|
|
@ -1387,7 +1387,7 @@ impl<'hir> Pat<'hir> {
|
|||
use PatKind::*;
|
||||
match self.kind {
|
||||
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true,
|
||||
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
|
||||
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it),
|
||||
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
|
||||
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
|
||||
Slice(before, slice, after) => {
|
||||
|
|
@ -1414,7 +1414,7 @@ impl<'hir> Pat<'hir> {
|
|||
use PatKind::*;
|
||||
match self.kind {
|
||||
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {}
|
||||
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
|
||||
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
|
||||
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
|
||||
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
|
||||
Slice(before, slice, after) => {
|
||||
|
|
@ -1566,6 +1566,9 @@ pub enum PatKind<'hir> {
|
|||
/// A literal.
|
||||
Lit(&'hir Expr<'hir>),
|
||||
|
||||
/// A guard pattern (e.g., `x if guard(x)`).
|
||||
Guard(&'hir Pat<'hir>, &'hir Expr<'hir>),
|
||||
|
||||
/// A range pattern (e.g., `1..=2` or `1..2`).
|
||||
Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd),
|
||||
|
||||
|
|
|
|||
|
|
@ -696,6 +696,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V:
|
|||
visit_opt!(visitor, visit_pat, slice_pattern);
|
||||
walk_list!(visitor, visit_pat, postpatterns);
|
||||
}
|
||||
PatKind::Guard(subpat, condition) => {
|
||||
try_visit!(visitor.visit_pat(subpat));
|
||||
try_visit!(visitor.visit_expr(condition));
|
||||
}
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1845,13 +1845,18 @@ pub(super) fn check_coroutine_obligations(
|
|||
|
||||
debug!(?typeck_results.coroutine_stalled_predicates);
|
||||
|
||||
let mode = if tcx.next_trait_solver_globally() {
|
||||
TypingMode::post_borrowck_analysis(tcx, def_id)
|
||||
} else {
|
||||
TypingMode::analysis_in_body(tcx, def_id)
|
||||
};
|
||||
|
||||
let infcx = tcx
|
||||
.infer_ctxt()
|
||||
// typeck writeback gives us predicates with their regions erased.
|
||||
// As borrowck already has checked lifetimes, we do not need to do it again.
|
||||
.ignoring_regions()
|
||||
// FIXME(#132279): This should eventually use the already defined hidden types.
|
||||
.build(TypingMode::analysis_in_body(tcx, def_id));
|
||||
.build(mode);
|
||||
|
||||
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
|
||||
for (predicate, cause) in &typeck_results.coroutine_stalled_predicates {
|
||||
|
|
@ -1864,12 +1869,14 @@ pub(super) fn check_coroutine_obligations(
|
|||
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
|
||||
}
|
||||
|
||||
// Check that any hidden types found when checking these stalled coroutine obligations
|
||||
// are valid.
|
||||
for (key, ty) in infcx.take_opaque_types() {
|
||||
let hidden_type = infcx.resolve_vars_if_possible(ty.hidden_type);
|
||||
let key = infcx.resolve_vars_if_possible(key);
|
||||
sanity_check_found_hidden_type(tcx, key, hidden_type)?;
|
||||
if !tcx.next_trait_solver_globally() {
|
||||
// Check that any hidden types found when checking these stalled coroutine obligations
|
||||
// are valid.
|
||||
for (key, ty) in infcx.take_opaque_types() {
|
||||
let hidden_type = infcx.resolve_vars_if_possible(ty.hidden_type);
|
||||
let key = infcx.resolve_vars_if_possible(key);
|
||||
sanity_check_found_hidden_type(tcx, key, hidden_type)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -654,6 +654,7 @@ fn resolve_local<'tcx>(
|
|||
/// | ( ..., P&, ... )
|
||||
/// | ... "|" P& "|" ...
|
||||
/// | box P&
|
||||
/// | P& if ...
|
||||
/// ```
|
||||
fn is_binding_pat(pat: &hir::Pat<'_>) -> bool {
|
||||
// Note that the code below looks for *explicit* refs only, that is, it won't
|
||||
|
|
@ -694,7 +695,9 @@ fn resolve_local<'tcx>(
|
|||
| PatKind::TupleStruct(_, subpats, _)
|
||||
| PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)),
|
||||
|
||||
PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat),
|
||||
PatKind::Box(subpat) | PatKind::Deref(subpat) | PatKind::Guard(subpat, _) => {
|
||||
is_binding_pat(subpat)
|
||||
}
|
||||
|
||||
PatKind::Ref(_, _)
|
||||
| PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..)
|
||||
|
|
|
|||
|
|
@ -128,21 +128,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained(
|
|||
for param in &impl_generics.own_params {
|
||||
match param.kind {
|
||||
ty::GenericParamDefKind::Lifetime => {
|
||||
let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
|
||||
if lifetimes_in_associated_types.contains(¶m_lt) // (*)
|
||||
&& !input_parameters.contains(¶m_lt)
|
||||
{
|
||||
let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
|
||||
span: tcx.def_span(param.def_id),
|
||||
param_name: param.name,
|
||||
param_def_kind: tcx.def_descr(param.def_id),
|
||||
const_param_note: false,
|
||||
const_param_note2: false,
|
||||
});
|
||||
diag.code(E0207);
|
||||
res = Err(diag.emit());
|
||||
}
|
||||
// (*) This is a horrible concession to reality. I think it'd be
|
||||
// This is a horrible concession to reality. I think it'd be
|
||||
// better to just ban unconstrained lifetimes outright, but in
|
||||
// practice people do non-hygienic macros like:
|
||||
//
|
||||
|
|
@ -160,6 +146,20 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained(
|
|||
// permit those, so long as the lifetimes aren't used in
|
||||
// associated types. I believe this is sound, because lifetimes
|
||||
// used elsewhere are not projected back out.
|
||||
let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
|
||||
if lifetimes_in_associated_types.contains(¶m_lt)
|
||||
&& !input_parameters.contains(¶m_lt)
|
||||
{
|
||||
let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
|
||||
span: tcx.def_span(param.def_id),
|
||||
param_name: param.name,
|
||||
param_def_kind: tcx.def_descr(param.def_id),
|
||||
const_param_note: false,
|
||||
const_param_note2: false,
|
||||
});
|
||||
diag.code(E0207);
|
||||
res = Err(diag.emit());
|
||||
}
|
||||
}
|
||||
ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => {
|
||||
// Enforced in `enforce_impl_non_lifetime_params_are_constrained`.
|
||||
|
|
|
|||
|
|
@ -1999,6 +1999,12 @@ impl<'a> State<'a> {
|
|||
self.commasep(Inconsistent, after, |s, p| s.print_pat(p));
|
||||
self.word("]");
|
||||
}
|
||||
PatKind::Guard(inner, cond) => {
|
||||
self.print_pat(inner);
|
||||
self.space();
|
||||
self.word_space("if");
|
||||
self.print_expr(cond);
|
||||
}
|
||||
PatKind::Err(_) => {
|
||||
self.popen();
|
||||
self.word("/*ERROR*/");
|
||||
|
|
|
|||
|
|
@ -456,6 +456,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Does not constitute a read.
|
||||
hir::PatKind::Wild => false,
|
||||
|
||||
// Might not constitute a read, since the condition might be false.
|
||||
hir::PatKind::Guard(_, _) => true,
|
||||
|
||||
// This is unnecessarily restrictive when the pattern that doesn't
|
||||
// constitute a read is unreachable.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -615,6 +615,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
| PatKind::Box(_)
|
||||
| PatKind::Deref(_)
|
||||
| PatKind::Ref(..)
|
||||
| PatKind::Guard(..)
|
||||
| PatKind::Wild
|
||||
| PatKind::Err(_) => {
|
||||
// If the PatKind is Or, Box, or Ref, the decision is made later
|
||||
|
|
@ -1737,7 +1738,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
}
|
||||
}
|
||||
|
||||
PatKind::Binding(.., Some(subpat)) => {
|
||||
PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => {
|
||||
self.cat_pattern(place_with_id, subpat, op)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,13 +40,25 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// FIXME: Move this check out of typeck, since it'll easily cycle when revealing opaques,
|
||||
/// and we shouldn't need to check anything here if the typeck results are tainted.
|
||||
pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
|
||||
let tcx = self.tcx;
|
||||
let dl = &tcx.data_layout;
|
||||
let span = tcx.hir().span(hir_id);
|
||||
let normalize = |ty| {
|
||||
let ty = self.resolve_vars_if_possible(ty);
|
||||
self.tcx.normalize_erasing_regions(self.typing_env(self.param_env), ty)
|
||||
if let Ok(ty) =
|
||||
self.tcx.try_normalize_erasing_regions(self.typing_env(self.param_env), ty)
|
||||
{
|
||||
ty
|
||||
} else {
|
||||
Ty::new_error_with_message(
|
||||
tcx,
|
||||
span,
|
||||
"tried to normalize non-wf type in check_transmute",
|
||||
)
|
||||
}
|
||||
};
|
||||
let from = normalize(from);
|
||||
let to = normalize(to);
|
||||
|
|
|
|||
|
|
@ -147,8 +147,23 @@ fn typeck_with_fallback<'tcx>(
|
|||
check_abi(tcx, span, fn_sig.abi());
|
||||
|
||||
// Compute the function signature from point of view of inside the fn.
|
||||
let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
|
||||
let fn_sig = fcx.normalize(body.value.span, fn_sig);
|
||||
let mut fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
|
||||
|
||||
// Normalize the input and output types one at a time, using a different
|
||||
// `WellFormedLoc` for each. We cannot call `normalize_associated_types`
|
||||
// on the entire `FnSig`, since this would use the same `WellFormedLoc`
|
||||
// for each type, preventing the HIR wf check from generating
|
||||
// a nice error message.
|
||||
let arg_span =
|
||||
|idx| decl.inputs.get(idx).map_or(decl.output.span(), |arg: &hir::Ty<'_>| arg.span);
|
||||
|
||||
fn_sig.inputs_and_output = tcx.mk_type_list_from_iter(
|
||||
fn_sig
|
||||
.inputs_and_output
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, ty)| fcx.normalize(arg_span(idx), ty)),
|
||||
);
|
||||
|
||||
check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params());
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use rustc_data_structures::sorted_map::SortedMap;
|
|||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
|
|
@ -690,6 +690,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
// Check if we wrote `Self::Assoc(1)` as if it were a tuple ctor.
|
||||
if let SelfSource::QPath(ty) = source
|
||||
&& let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
|
||||
&& let Res::SelfTyAlias { alias_to: impl_def_id, .. } = path.res
|
||||
&& let DefKind::Impl { .. } = self.tcx.def_kind(impl_def_id)
|
||||
&& let Some(candidate) = tcx.associated_items(impl_def_id).find_by_name_and_kind(
|
||||
self.tcx,
|
||||
item_name,
|
||||
ty::AssocKind::Type,
|
||||
impl_def_id,
|
||||
)
|
||||
&& let Some(adt_def) = tcx.type_of(candidate.def_id).skip_binder().ty_adt_def()
|
||||
&& adt_def.is_struct()
|
||||
&& adt_def.non_enum_variant().ctor_kind() == Some(CtorKind::Fn)
|
||||
{
|
||||
let def_path = tcx.def_path_str(adt_def.did());
|
||||
err.span_suggestion(
|
||||
ty.span.to(item_name.span),
|
||||
format!("to construct a value of type `{}`, use the explicit path", def_path),
|
||||
def_path,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
err
|
||||
};
|
||||
if tcx.sess.source_map().is_multiline(sugg_span) {
|
||||
|
|
|
|||
|
|
@ -284,6 +284,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
PatKind::Struct(ref qpath, fields, has_rest_pat) => {
|
||||
self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
|
||||
}
|
||||
PatKind::Guard(pat, cond) => {
|
||||
self.check_pat(pat, expected, pat_info);
|
||||
self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {});
|
||||
expected
|
||||
}
|
||||
PatKind::Or(pats) => {
|
||||
for pat in pats {
|
||||
self.check_pat(pat, expected, pat_info);
|
||||
|
|
@ -422,7 +427,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// An OR-pattern just propagates to each individual alternative.
|
||||
// This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`.
|
||||
// In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`.
|
||||
| PatKind::Or(_) => AdjustMode::Pass,
|
||||
| PatKind::Or(_)
|
||||
// Like or-patterns, guard patterns just propogate to their subpatterns.
|
||||
| PatKind::Guard(..) => AdjustMode::Pass,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -901,6 +908,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
PatKind::Struct(..)
|
||||
| PatKind::TupleStruct(..)
|
||||
| PatKind::Or(..)
|
||||
| PatKind::Guard(..)
|
||||
| PatKind::Tuple(..)
|
||||
| PatKind::Slice(..) => "binding",
|
||||
|
||||
|
|
|
|||
|
|
@ -582,6 +582,7 @@ fn test_codegen_options_tracking_hash() {
|
|||
untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe")));
|
||||
untracked!(extra_filename, String::from("extra-filename"));
|
||||
untracked!(incremental, Some(String::from("abc")));
|
||||
untracked!(inline_threshold, Some(0xf007ba11));
|
||||
// `link_arg` is omitted because it just forwards to `link_args`.
|
||||
untracked!(link_args, vec![String::from("abc"), String::from("def")]);
|
||||
untracked!(link_self_contained, LinkSelfContained::on());
|
||||
|
|
@ -613,7 +614,6 @@ fn test_codegen_options_tracking_hash() {
|
|||
tracked!(embed_bitcode, false);
|
||||
tracked!(force_frame_pointers, FramePointer::Always);
|
||||
tracked!(force_unwind_tables, Some(true));
|
||||
tracked!(inline_threshold, Some(0xf007ba11));
|
||||
tracked!(instrument_coverage, InstrumentCoverage::Yes);
|
||||
tracked!(link_dead_code, Some(true));
|
||||
tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
|
||||
|
|
@ -782,6 +782,7 @@ fn test_unstable_options_tracking_hash() {
|
|||
tracked!(dwarf_version, Some(5));
|
||||
tracked!(embed_source, true);
|
||||
tracked!(emit_thin_lto, false);
|
||||
tracked!(emscripten_wasm_eh, true);
|
||||
tracked!(export_executable_symbols, true);
|
||||
tracked!(fewer_names, Some(true));
|
||||
tracked!(fixed_x18, true);
|
||||
|
|
|
|||
|
|
@ -1244,8 +1244,8 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
|
|||
|
||||
declare_lint! {
|
||||
/// The `unreachable_pub` lint triggers for `pub` items not reachable from other crates - that
|
||||
/// means neither directly accessible, nor reexported, nor leaked through things like return
|
||||
/// types.
|
||||
/// means neither directly accessible, nor reexported (with `pub use`), nor leaked through
|
||||
/// things like return types (which the [`unnameable_types`] lint can detect if desired).
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
|
|
@ -1272,8 +1272,10 @@ declare_lint! {
|
|||
/// intent that the item is only visible within its own crate.
|
||||
///
|
||||
/// This lint is "allow" by default because it will trigger for a large
|
||||
/// amount existing Rust code, and has some false-positives. Eventually it
|
||||
/// amount of existing Rust code, and has some false-positives. Eventually it
|
||||
/// is desired for this to become warn-by-default.
|
||||
///
|
||||
/// [`unnameable_types`]: #unnameable-types
|
||||
pub UNREACHABLE_PUB,
|
||||
Allow,
|
||||
"`pub` items not reachable from crate root"
|
||||
|
|
|
|||
|
|
@ -234,10 +234,10 @@ declare_lint! {
|
|||
declare_lint_pass!(NonSnakeCase => [NON_SNAKE_CASE]);
|
||||
|
||||
impl NonSnakeCase {
|
||||
fn to_snake_case(mut str: &str) -> String {
|
||||
fn to_snake_case(mut name: &str) -> String {
|
||||
let mut words = vec![];
|
||||
// Preserve leading underscores
|
||||
str = str.trim_start_matches(|c: char| {
|
||||
name = name.trim_start_matches(|c: char| {
|
||||
if c == '_' {
|
||||
words.push(String::new());
|
||||
true
|
||||
|
|
@ -245,7 +245,7 @@ impl NonSnakeCase {
|
|||
false
|
||||
}
|
||||
});
|
||||
for s in str.split('_') {
|
||||
for s in name.split('_') {
|
||||
let mut last_upper = false;
|
||||
let mut buf = String::new();
|
||||
if s.is_empty() {
|
||||
|
|
|
|||
|
|
@ -4362,7 +4362,7 @@ declare_lint! {
|
|||
/// ### Explanation
|
||||
///
|
||||
/// It is often expected that if you can obtain an object of type `T`, then
|
||||
/// you can name the type `T` as well, this lint attempts to enforce this rule.
|
||||
/// you can name the type `T` as well; this lint attempts to enforce this rule.
|
||||
/// The recommended action is to either reexport the type properly to make it nameable,
|
||||
/// or document that users are not supposed to be able to name it for one reason or another.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1389,20 +1389,14 @@ static bool clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) {
|
|||
return ClearDSOLocalOnDeclarations;
|
||||
}
|
||||
|
||||
extern "C" bool LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data,
|
||||
extern "C" void LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data,
|
||||
LLVMModuleRef M,
|
||||
LLVMTargetMachineRef TM) {
|
||||
Module &Mod = *unwrap(M);
|
||||
TargetMachine &Target = *unwrap(TM);
|
||||
|
||||
bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target);
|
||||
bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal);
|
||||
|
||||
if (error) {
|
||||
LLVMRustSetLastError("renameModuleForThinLTO failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal);
|
||||
}
|
||||
|
||||
extern "C" bool
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@ using namespace llvm;
|
|||
using namespace llvm::sys;
|
||||
using namespace llvm::object;
|
||||
|
||||
// This opcode is an LLVM detail that could hypothetically change (?), so
|
||||
// verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM.
|
||||
static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000);
|
||||
|
||||
// LLVMAtomicOrdering is already an enum - don't create another
|
||||
// one.
|
||||
static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) {
|
||||
|
|
@ -1397,18 +1401,6 @@ LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location,
|
|||
return wrap(NewLoc.has_value() ? NewLoc.value() : nullptr);
|
||||
}
|
||||
|
||||
extern "C" uint64_t LLVMRustDIBuilderCreateOpDeref() {
|
||||
return dwarf::DW_OP_deref;
|
||||
}
|
||||
|
||||
extern "C" uint64_t LLVMRustDIBuilderCreateOpPlusUconst() {
|
||||
return dwarf::DW_OP_plus_uconst;
|
||||
}
|
||||
|
||||
extern "C" uint64_t LLVMRustDIBuilderCreateOpLLVMFragment() {
|
||||
return dwarf::DW_OP_LLVM_fragment;
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) {
|
||||
auto OS = RawRustStringOstream(Str);
|
||||
unwrap<llvm::Type>(Ty)->print(OS);
|
||||
|
|
|
|||
|
|
@ -130,11 +130,11 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
|
|||
|
||||
let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
|
||||
match cfg.backtrace {
|
||||
Ok(str) => {
|
||||
Ok(backtrace_target) => {
|
||||
let fmt_layer = tracing_subscriber::fmt::layer()
|
||||
.with_writer(io::stderr)
|
||||
.without_time()
|
||||
.event_format(BacktraceFormatter { backtrace_target: str });
|
||||
.event_format(BacktraceFormatter { backtrace_target });
|
||||
let subscriber = subscriber.with(fmt_layer);
|
||||
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,14 +156,14 @@ impl Entries {
|
|||
Entries { map: HashMap::with_capacity(capacity) }
|
||||
}
|
||||
|
||||
fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 {
|
||||
if let Some(prev) = self.map.get(str) {
|
||||
errors.error(span, format!("Symbol `{str}` is duplicated"));
|
||||
fn insert(&mut self, span: Span, s: &str, errors: &mut Errors) -> u32 {
|
||||
if let Some(prev) = self.map.get(s) {
|
||||
errors.error(span, format!("Symbol `{s}` is duplicated"));
|
||||
errors.error(prev.span_of_name, "location of previous definition".to_string());
|
||||
prev.idx
|
||||
} else {
|
||||
let idx = self.len();
|
||||
self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span });
|
||||
self.map.insert(s.to_string(), Preinterned { idx, span_of_name: span });
|
||||
idx
|
||||
}
|
||||
}
|
||||
|
|
@ -192,14 +192,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
|
|||
let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10);
|
||||
let mut prev_key: Option<(Span, String)> = None;
|
||||
|
||||
let mut check_order = |span: Span, str: &str, errors: &mut Errors| {
|
||||
let mut check_order = |span: Span, s: &str, errors: &mut Errors| {
|
||||
if let Some((prev_span, ref prev_str)) = prev_key {
|
||||
if str < prev_str {
|
||||
errors.error(span, format!("Symbol `{str}` must precede `{prev_str}`"));
|
||||
if s < prev_str {
|
||||
errors.error(span, format!("Symbol `{s}` must precede `{prev_str}`"));
|
||||
errors.error(prev_span, format!("location of previous symbol `{prev_str}`"));
|
||||
}
|
||||
}
|
||||
prev_key = Some((span, str.to_string()));
|
||||
prev_key = Some((span, s.to_string()));
|
||||
};
|
||||
|
||||
// Generate the listed keywords.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ edition = "2021"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
derive-where = "1.2.7"
|
||||
either = "1.5.0"
|
||||
field-offset = "0.3.5"
|
||||
gsgdt = "0.1.2"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ pub struct CodegenFnAttrs {
|
|||
pub link_ordinal: Option<u16>,
|
||||
/// The `#[target_feature(enable = "...")]` attribute and the enabled
|
||||
/// features (only enabled features are supported right now).
|
||||
/// Implied target features have already been applied.
|
||||
pub target_features: Vec<TargetFeature>,
|
||||
/// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
|
||||
pub linkage: Option<Linkage>,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
use std::cell::Cell;
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
use derive_where::derive_where;
|
||||
use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
|
|
@ -225,29 +224,22 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16);
|
|||
/// See also `rustc_const_eval::borrow_check::constraints`.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)]
|
||||
#[derive_where(PartialOrd, Ord)]
|
||||
pub enum ConstraintCategory<'tcx> {
|
||||
Return(ReturnConstraint),
|
||||
Yield,
|
||||
UseAsConst,
|
||||
UseAsStatic,
|
||||
TypeAnnotation,
|
||||
TypeAnnotation(AnnotationSource),
|
||||
Cast {
|
||||
/// Whether this cast is a coercion that was automatically inserted by the compiler.
|
||||
is_implicit_coercion: bool,
|
||||
/// Whether this is an unsizing coercion and if yes, this contains the target type.
|
||||
/// Region variables are erased to ReErased.
|
||||
#[derive_where(skip)]
|
||||
unsize_to: Option<Ty<'tcx>>,
|
||||
},
|
||||
|
||||
/// A constraint that came from checking the body of a closure.
|
||||
///
|
||||
/// We try to get the category that the closure used when reporting this.
|
||||
ClosureBounds,
|
||||
|
||||
/// Contains the function type if available.
|
||||
CallArgument(#[derive_where(skip)] Option<Ty<'tcx>>),
|
||||
CallArgument(Option<Ty<'tcx>>),
|
||||
CopyBound,
|
||||
SizedBound,
|
||||
Assignment,
|
||||
|
|
@ -276,13 +268,22 @@ pub enum ConstraintCategory<'tcx> {
|
|||
IllegalUniverse,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)]
|
||||
pub enum ReturnConstraint {
|
||||
Normal,
|
||||
ClosureUpvar(FieldIdx),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)]
|
||||
pub enum AnnotationSource {
|
||||
Ascription,
|
||||
Declaration,
|
||||
OpaqueCast,
|
||||
GenericArg,
|
||||
}
|
||||
|
||||
/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing
|
||||
/// that must outlive some region.
|
||||
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||
|
|
|
|||
|
|
@ -455,6 +455,8 @@ impl BorrowKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether borrows represented by this kind are allowed to be split into separate
|
||||
/// Reservation and Activation phases.
|
||||
pub fn allows_two_phase_borrow(&self) -> bool {
|
||||
match *self {
|
||||
BorrowKind::Shared
|
||||
|
|
|
|||
|
|
@ -2357,7 +2357,7 @@ rustc_queries! {
|
|||
}
|
||||
|
||||
/// Returns the Rust target features for the current target. These are not always the same as LLVM target features!
|
||||
query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::StabilityComputed> {
|
||||
query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::Stability> {
|
||||
arena_cache
|
||||
eval_always
|
||||
desc { "looking up Rust target features" }
|
||||
|
|
|
|||
|
|
@ -125,6 +125,15 @@ impl<'tcx> ObligationCause<'tcx> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn derived_host_cause(
|
||||
mut self,
|
||||
parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
|
||||
variant: impl FnOnce(DerivedHostCause<'tcx>) -> ObligationCauseCode<'tcx>,
|
||||
) -> ObligationCause<'tcx> {
|
||||
self.code = variant(DerivedHostCause { parent_host_pred, parent_code: self.code }).into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> {
|
||||
match self.code() {
|
||||
ObligationCauseCode::MatchImpl(cause, _) => cause.to_constraint_category(),
|
||||
|
|
@ -278,6 +287,14 @@ pub enum ObligationCauseCode<'tcx> {
|
|||
/// Derived obligation for WF goals.
|
||||
WellFormedDerived(DerivedCause<'tcx>),
|
||||
|
||||
/// Derived obligation (i.e. `where` clause) on an user-provided impl
|
||||
/// or a trait alias.
|
||||
ImplDerivedHost(Box<ImplDerivedHostCause<'tcx>>),
|
||||
|
||||
/// Derived obligation (i.e. `where` clause) on an user-provided impl
|
||||
/// or a trait alias.
|
||||
BuiltinDerivedHost(DerivedHostCause<'tcx>),
|
||||
|
||||
/// Derived obligation refined to point at a specific argument in
|
||||
/// a call or method expression.
|
||||
FunctionArg {
|
||||
|
|
@ -437,36 +454,38 @@ pub enum WellFormedLoc {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
#[derive(TypeVisitable, TypeFoldable)]
|
||||
pub struct ImplDerivedCause<'tcx> {
|
||||
pub derived: DerivedCause<'tcx>,
|
||||
/// The `DefId` of the `impl` that gave rise to the `derived` obligation.
|
||||
/// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl,
|
||||
/// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle
|
||||
/// that exceptional case where appropriate.
|
||||
pub impl_or_alias_def_id: DefId,
|
||||
/// The index of the derived predicate in the parent impl's predicates.
|
||||
pub impl_def_predicate_index: Option<usize>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationCauseCode<'tcx> {
|
||||
/// Returns the base obligation, ignoring derived obligations.
|
||||
pub fn peel_derives(&self) -> &Self {
|
||||
let mut base_cause = self;
|
||||
while let Some((parent_code, _)) = base_cause.parent() {
|
||||
while let Some(parent_code) = base_cause.parent() {
|
||||
base_cause = parent_code;
|
||||
}
|
||||
base_cause
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<&Self> {
|
||||
match self {
|
||||
ObligationCauseCode::FunctionArg { parent_code, .. } => Some(parent_code),
|
||||
ObligationCauseCode::BuiltinDerived(derived)
|
||||
| ObligationCauseCode::WellFormedDerived(derived)
|
||||
| ObligationCauseCode::ImplDerived(box ImplDerivedCause { derived, .. }) => {
|
||||
Some(&derived.parent_code)
|
||||
}
|
||||
ObligationCauseCode::BuiltinDerivedHost(derived)
|
||||
| ObligationCauseCode::ImplDerivedHost(box ImplDerivedHostCause { derived, .. }) => {
|
||||
Some(&derived.parent_code)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base obligation and the base trait predicate, if any, ignoring
|
||||
/// derived obligations.
|
||||
pub fn peel_derives_with_predicate(&self) -> (&Self, Option<ty::PolyTraitPredicate<'tcx>>) {
|
||||
let mut base_cause = self;
|
||||
let mut base_trait_pred = None;
|
||||
while let Some((parent_code, parent_pred)) = base_cause.parent() {
|
||||
while let Some((parent_code, parent_pred)) = base_cause.parent_with_predicate() {
|
||||
base_cause = parent_code;
|
||||
if let Some(parent_pred) = parent_pred {
|
||||
base_trait_pred = Some(parent_pred);
|
||||
|
|
@ -476,7 +495,7 @@ impl<'tcx> ObligationCauseCode<'tcx> {
|
|||
(base_cause, base_trait_pred)
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> {
|
||||
pub fn parent_with_predicate(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> {
|
||||
match self {
|
||||
ObligationCauseCode::FunctionArg { parent_code, .. } => Some((parent_code, None)),
|
||||
ObligationCauseCode::BuiltinDerived(derived)
|
||||
|
|
@ -573,6 +592,42 @@ pub struct DerivedCause<'tcx> {
|
|||
pub parent_code: InternedObligationCauseCode<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
#[derive(TypeVisitable, TypeFoldable)]
|
||||
pub struct ImplDerivedCause<'tcx> {
|
||||
pub derived: DerivedCause<'tcx>,
|
||||
/// The `DefId` of the `impl` that gave rise to the `derived` obligation.
|
||||
/// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl,
|
||||
/// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle
|
||||
/// that exceptional case where appropriate.
|
||||
pub impl_or_alias_def_id: DefId,
|
||||
/// The index of the derived predicate in the parent impl's predicates.
|
||||
pub impl_def_predicate_index: Option<usize>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
#[derive(TypeVisitable, TypeFoldable)]
|
||||
pub struct DerivedHostCause<'tcx> {
|
||||
/// The trait predicate of the parent obligation that led to the
|
||||
/// current obligation. Note that only trait obligations lead to
|
||||
/// derived obligations, so we just store the trait predicate here
|
||||
/// directly.
|
||||
pub parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
|
||||
|
||||
/// The parent trait had this cause.
|
||||
pub parent_code: InternedObligationCauseCode<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
|
||||
#[derive(TypeVisitable, TypeFoldable)]
|
||||
pub struct ImplDerivedHostCause<'tcx> {
|
||||
pub derived: DerivedHostCause<'tcx>,
|
||||
/// The `DefId` of the `impl` that gave rise to the `derived` obligation.
|
||||
pub impl_def_id: DefId,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, TypeVisitable)]
|
||||
pub enum SelectionError<'tcx> {
|
||||
/// The trait is not implemented.
|
||||
|
|
|
|||
|
|
@ -634,6 +634,28 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PolyProjectionPredicate<'tcx>> for Clause<'t
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>>
|
||||
for Predicate<'tcx>
|
||||
{
|
||||
fn upcast_from(
|
||||
from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Self {
|
||||
from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>>
|
||||
for Clause<'tcx>
|
||||
{
|
||||
fn upcast_from(
|
||||
from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Self {
|
||||
from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UpcastFrom<TyCtxt<'tcx>, NormalizesTo<'tcx>> for Predicate<'tcx> {
|
||||
fn upcast_from(from: NormalizesTo<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
|
||||
PredicateKind::NormalizesTo(from).upcast(tcx)
|
||||
|
|
|
|||
|
|
@ -45,10 +45,25 @@ pub trait Printer<'tcx>: Sized {
|
|||
&mut self,
|
||||
impl_def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<(), PrintError> {
|
||||
self.default_print_impl_path(impl_def_id, args, self_ty, trait_ref)
|
||||
let tcx = self.tcx();
|
||||
let self_ty = tcx.type_of(impl_def_id);
|
||||
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
|
||||
let (self_ty, impl_trait_ref) = if tcx.generics_of(impl_def_id).count() <= args.len() {
|
||||
(
|
||||
self_ty.instantiate(tcx, args),
|
||||
impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(tcx, args)),
|
||||
)
|
||||
} else {
|
||||
// We are probably printing a nested item inside of an impl.
|
||||
// Use the identity substitutions for the impl.
|
||||
(
|
||||
self_ty.instantiate_identity(),
|
||||
impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()),
|
||||
)
|
||||
};
|
||||
|
||||
self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref)
|
||||
}
|
||||
|
||||
fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>;
|
||||
|
|
@ -107,23 +122,7 @@ pub trait Printer<'tcx>: Sized {
|
|||
self.path_crate(def_id.krate)
|
||||
}
|
||||
|
||||
DefPathData::Impl => {
|
||||
let generics = self.tcx().generics_of(def_id);
|
||||
let self_ty = self.tcx().type_of(def_id);
|
||||
let impl_trait_ref = self.tcx().impl_trait_ref(def_id);
|
||||
let (self_ty, impl_trait_ref) = if args.len() >= generics.count() {
|
||||
(
|
||||
self_ty.instantiate(self.tcx(), args),
|
||||
impl_trait_ref.map(|i| i.instantiate(self.tcx(), args)),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
self_ty.instantiate_identity(),
|
||||
impl_trait_ref.map(|i| i.instantiate_identity()),
|
||||
)
|
||||
};
|
||||
self.print_impl_path(def_id, args, self_ty, impl_trait_ref)
|
||||
}
|
||||
DefPathData::Impl => self.print_impl_path(def_id, args),
|
||||
|
||||
_ => {
|
||||
let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id };
|
||||
|
|
@ -201,7 +200,6 @@ pub trait Printer<'tcx>: Sized {
|
|||
fn default_print_impl_path(
|
||||
&mut self,
|
||||
impl_def_id: DefId,
|
||||
_args: &'tcx [GenericArg<'tcx>],
|
||||
self_ty: Ty<'tcx>,
|
||||
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<(), PrintError> {
|
||||
|
|
|
|||
|
|
@ -17,26 +17,29 @@ pub fn find_self_call<'tcx>(
|
|||
debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator);
|
||||
if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
|
||||
&body[block].terminator
|
||||
&& let Operand::Constant(box ConstOperand { const_, .. }) = func
|
||||
&& let ty::FnDef(def_id, fn_args) = *const_.ty().kind()
|
||||
&& let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
|
||||
tcx.opt_associated_item(def_id)
|
||||
&& let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] =
|
||||
**args
|
||||
{
|
||||
debug!("find_self_call: func={:?}", func);
|
||||
if let Operand::Constant(box ConstOperand { const_, .. }) = func {
|
||||
if let ty::FnDef(def_id, fn_args) = *const_.ty().kind() {
|
||||
if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
|
||||
tcx.opt_associated_item(def_id)
|
||||
{
|
||||
debug!("find_self_call: args={:?}", fn_args);
|
||||
if let [
|
||||
Spanned {
|
||||
node: Operand::Move(self_place) | Operand::Copy(self_place), ..
|
||||
},
|
||||
..,
|
||||
] = **args
|
||||
{
|
||||
if self_place.as_local() == Some(local) {
|
||||
return Some((def_id, fn_args));
|
||||
}
|
||||
}
|
||||
}
|
||||
if self_place.as_local() == Some(local) {
|
||||
return Some((def_id, fn_args));
|
||||
}
|
||||
|
||||
// Handle the case where `self_place` gets reborrowed.
|
||||
// This happens when the receiver is `&T`.
|
||||
for stmt in &body[block].statements {
|
||||
if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind
|
||||
&& let Some(reborrow_local) = place.as_local()
|
||||
&& self_place.as_local() == Some(reborrow_local)
|
||||
&& let Rvalue::Ref(_, _, deref_place) = rvalue
|
||||
&& let PlaceRef { local: deref_local, projection: [ProjectionElem::Deref] } =
|
||||
deref_place.as_ref()
|
||||
&& deref_local == local
|
||||
{
|
||||
return Some((def_id, fn_args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1131,15 +1131,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
/// Schedule emission of a backwards incompatible drop lint hint.
|
||||
/// Applicable only to temporary values for now.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn schedule_backwards_incompatible_drop(
|
||||
&mut self,
|
||||
span: Span,
|
||||
region_scope: region::Scope,
|
||||
local: Local,
|
||||
) {
|
||||
if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) {
|
||||
return;
|
||||
}
|
||||
// Note that we are *not* gating BIDs here on whether they have significant destructor.
|
||||
// We need to know all of them so that we can capture potential borrow-checking errors.
|
||||
for scope in self.scopes.scopes.iter_mut().rev() {
|
||||
// Since we are inserting linting MIR statement, we have to invalidate the caches
|
||||
scope.invalidate_cache();
|
||||
|
|
|
|||
|
|
@ -1086,14 +1086,7 @@ fn find_fallback_pattern_typo<'tcx>(
|
|||
let vis = cx.tcx.visibility(item.owner_id);
|
||||
if vis.is_accessible_from(parent, cx.tcx) {
|
||||
accessible.push(item_name);
|
||||
let path = if item_name == name {
|
||||
// We know that the const wasn't in scope because it has the exact
|
||||
// same name, so we suggest the full path.
|
||||
with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id))
|
||||
} else {
|
||||
// The const is likely just typoed, and nothing else.
|
||||
cx.tcx.def_path_str(item.owner_id)
|
||||
};
|
||||
let path = with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id));
|
||||
accessible_path.push(path);
|
||||
} else if name == item_name {
|
||||
// The const exists somewhere in this crate, but it can't be imported
|
||||
|
|
|
|||
|
|
@ -435,6 +435,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
|
||||
hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) },
|
||||
|
||||
// FIXME(guard_patterns): implement guard pattern lowering
|
||||
hir::PatKind::Guard(pat, _) => self.lower_pattern(pat).kind,
|
||||
|
||||
hir::PatKind::Err(guar) => PatKind::Error(guar),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,8 @@ fn build_ptr_tys<'tcx>(
|
|||
pub(super) fn build_projection<'tcx>(
|
||||
unique_ty: Ty<'tcx>,
|
||||
nonnull_ty: Ty<'tcx>,
|
||||
ptr_ty: Ty<'tcx>,
|
||||
) -> [PlaceElem<'tcx>; 3] {
|
||||
[
|
||||
PlaceElem::Field(FieldIdx::ZERO, unique_ty),
|
||||
PlaceElem::Field(FieldIdx::ZERO, nonnull_ty),
|
||||
PlaceElem::Field(FieldIdx::ZERO, ptr_ty),
|
||||
]
|
||||
) -> [PlaceElem<'tcx>; 2] {
|
||||
[PlaceElem::Field(FieldIdx::ZERO, unique_ty), PlaceElem::Field(FieldIdx::ZERO, nonnull_ty)]
|
||||
}
|
||||
|
||||
struct ElaborateBoxDerefVisitor<'a, 'tcx> {
|
||||
|
|
@ -75,10 +70,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> {
|
|||
self.patch.add_assign(
|
||||
location,
|
||||
Place::from(ptr_local),
|
||||
Rvalue::Use(Operand::Copy(
|
||||
Place::from(place.local)
|
||||
.project_deeper(&build_projection(unique_ty, nonnull_ty, ptr_ty), tcx),
|
||||
)),
|
||||
Rvalue::Cast(
|
||||
CastKind::Transmute,
|
||||
Operand::Copy(
|
||||
Place::from(place.local)
|
||||
.project_deeper(&build_projection(unique_ty, nonnull_ty), tcx),
|
||||
),
|
||||
ptr_ty,
|
||||
),
|
||||
);
|
||||
|
||||
place.local = ptr_local;
|
||||
|
|
@ -133,8 +132,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
|
|||
let (unique_ty, nonnull_ty, ptr_ty) =
|
||||
build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did);
|
||||
|
||||
new_projections
|
||||
.extend_from_slice(&build_projection(unique_ty, nonnull_ty, ptr_ty));
|
||||
new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty));
|
||||
// While we can't project into `NonNull<_>` in a basic block
|
||||
// due to MCP#807, this is debug info where it's fine.
|
||||
new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty));
|
||||
new_projections.push(PlaceElem::Deref);
|
||||
} else if let Some(new_projections) = new_projections.as_mut() {
|
||||
// Keep building up our projections list once we've started it.
|
||||
|
|
|
|||
|
|
@ -351,6 +351,11 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME(typing_env): This should be able to reveal the opaques local to the
|
||||
// body using the typeck results.
|
||||
let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id);
|
||||
|
||||
// ## About BIDs in blocks ##
|
||||
// Track the set of blocks that contain a backwards-incompatible drop (BID)
|
||||
// and, for each block, the vector of locations.
|
||||
|
|
@ -358,7 +363,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
|
|||
// We group them per-block because they tend to scheduled in the same drop ladder block.
|
||||
let mut bid_per_block = IndexMap::default();
|
||||
let mut bid_places = UnordSet::new();
|
||||
let typing_env = ty::TypingEnv::post_analysis(tcx, def_id);
|
||||
|
||||
let mut ty_dropped_components = UnordMap::default();
|
||||
for (block, data) in body.basic_blocks.iter_enumerated() {
|
||||
for (statement_index, stmt) in data.statements.iter().enumerate() {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ where
|
|||
|ecx| {
|
||||
// Const conditions must hold for the implied const bound to hold.
|
||||
ecx.add_goals(
|
||||
GoalSource::Misc,
|
||||
GoalSource::AliasBoundConstCondition,
|
||||
cx.const_conditions(alias_ty.def_id)
|
||||
.iter_instantiated(cx, alias_ty.args)
|
||||
.map(|trait_ref| {
|
||||
|
|
@ -353,7 +353,7 @@ where
|
|||
|
||||
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
||||
ecx.add_goals(
|
||||
GoalSource::Misc,
|
||||
GoalSource::AliasBoundConstCondition,
|
||||
const_conditions.into_iter().map(|trait_ref| {
|
||||
goal.with(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
|
|||
Deref,
|
||||
Ref,
|
||||
Lit,
|
||||
Guard,
|
||||
Range,
|
||||
Slice,
|
||||
Err
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue