commit
79d4cc9d7c
778 changed files with 8935 additions and 5631 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
|
@ -47,3 +47,7 @@
|
|||
path = src/tools/rustc-perf
|
||||
url = https://github.com/rust-lang/rustc-perf.git
|
||||
shallow = true
|
||||
[submodule "src/tools/enzyme"]
|
||||
path = src/tools/enzyme
|
||||
url = https://github.com/EnzymeAD/Enzyme.git
|
||||
shallow = true
|
||||
|
|
|
|||
|
|
@ -4167,6 +4167,7 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_index",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_session",
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ Compiler
|
|||
- [Add Tier 3 `std` Xtensa targets:](https://github.com/rust-lang/rust/pull/126380/) `xtensa-esp32-espidf`, `xtensa-esp32s2-espidf`, `xtensa-esp32s3-espidf`
|
||||
- [Add Tier 3 i686 Redox OS target:](https://github.com/rust-lang/rust/pull/126192/) `i686-unknown-redox`
|
||||
- [Promote `arm64ec-pc-windows-msvc` to Tier 2.](https://github.com/rust-lang/rust/pull/126039/)
|
||||
- [Promote `wasm32-wasip2` to Tier 2.](https://github.com/rust-lang/rust/pull/126967/)
|
||||
- [Promote `loongarch64-unknown-linux-musl` to Tier 2 with host tools.](https://github.com/rust-lang/rust/pull/126298/)
|
||||
- [Enable full tools and profiler for LoongArch Linux targets.](https://github.com/rust-lang/rust/pull/127078/)
|
||||
- [Unconditionally warn on usage of `wasm32-wasi`.](https://github.com/rust-lang/rust/pull/126662/) (see compatibility note below)
|
||||
|
|
@ -100,6 +99,9 @@ Compatibility Notes
|
|||
The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
|
||||
|
||||
* The new sort implementations may panic if a type's implementation of [`Ord`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) (or the given comparison function) does not implement a [total order](https://en.wikipedia.org/wiki/Total_order) as the trait requires. `Ord`'s supertraits (`PartialOrd`, `Eq`, and `PartialEq`) must also be consistent. The previous implementations would not "notice" any problem, but the new implementations have a good chance of detecting inconsistencies, throwing a panic rather than returning knowingly unsorted data.
|
||||
* [In very rare cases, a change in the internal evaluation order of the trait
|
||||
solver may result in new fatal overflow errors.](https://github.com/rust-lang/rust/pull/126128)
|
||||
|
||||
|
||||
<a id="1.81.0-Internal-Changes"></a>
|
||||
|
||||
|
|
|
|||
|
|
@ -752,7 +752,7 @@ fn visit_lazy_tts<T: MutVisitor>(vis: &mut T, lazy_tts: &mut Option<LazyAttrToke
|
|||
pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
|
||||
let Token { kind, span } = t;
|
||||
match kind {
|
||||
token::Ident(name, _ /*raw*/) | token::Lifetime(name) => {
|
||||
token::Ident(name, _is_raw) | token::Lifetime(name, _is_raw) => {
|
||||
let mut ident = Ident::new(*name, *span);
|
||||
vis.visit_ident(&mut ident);
|
||||
*name = ident.name;
|
||||
|
|
@ -762,7 +762,7 @@ pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
|
|||
token::NtIdent(ident, _is_raw) => {
|
||||
vis.visit_ident(ident);
|
||||
}
|
||||
token::NtLifetime(ident) => {
|
||||
token::NtLifetime(ident, _is_raw) => {
|
||||
vis.visit_ident(ident);
|
||||
}
|
||||
token::Interpolated(nt) => {
|
||||
|
|
|
|||
|
|
@ -331,11 +331,11 @@ pub enum TokenKind {
|
|||
/// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
|
||||
/// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
|
||||
/// treat regular and interpolated lifetime identifiers in the same way.
|
||||
Lifetime(Symbol),
|
||||
Lifetime(Symbol, IdentIsRaw),
|
||||
/// This identifier (and its span) is the lifetime passed to the
|
||||
/// declarative macro. The span in the surrounding `Token` is the span of
|
||||
/// the `lifetime` metavariable in the macro's RHS.
|
||||
NtLifetime(Ident),
|
||||
NtLifetime(Ident, IdentIsRaw),
|
||||
|
||||
/// An embedded AST node, as produced by a macro. This only exists for
|
||||
/// historical reasons. We'd like to get rid of it, for multiple reasons.
|
||||
|
|
@ -458,7 +458,7 @@ impl Token {
|
|||
/// if they keep spans or perform edition checks.
|
||||
pub fn uninterpolated_span(&self) -> Span {
|
||||
match self.kind {
|
||||
NtIdent(ident, _) | NtLifetime(ident) => ident.span,
|
||||
NtIdent(ident, _) | NtLifetime(ident, _) => ident.span,
|
||||
Interpolated(ref nt) => nt.use_span(),
|
||||
_ => self.span,
|
||||
}
|
||||
|
|
@ -661,7 +661,9 @@ impl Token {
|
|||
pub fn uninterpolate(&self) -> Cow<'_, Token> {
|
||||
match self.kind {
|
||||
NtIdent(ident, is_raw) => Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span)),
|
||||
NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)),
|
||||
NtLifetime(ident, is_raw) => {
|
||||
Cow::Owned(Token::new(Lifetime(ident.name, is_raw), ident.span))
|
||||
}
|
||||
_ => Cow::Borrowed(self),
|
||||
}
|
||||
}
|
||||
|
|
@ -679,11 +681,11 @@ impl Token {
|
|||
|
||||
/// Returns a lifetime identifier if this token is a lifetime.
|
||||
#[inline]
|
||||
pub fn lifetime(&self) -> Option<Ident> {
|
||||
pub fn lifetime(&self) -> Option<(Ident, IdentIsRaw)> {
|
||||
// We avoid using `Token::uninterpolate` here because it's slow.
|
||||
match self.kind {
|
||||
Lifetime(name) => Some(Ident::new(name, self.span)),
|
||||
NtLifetime(ident) => Some(ident),
|
||||
Lifetime(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
|
||||
NtLifetime(ident, is_raw) => Some((ident, is_raw)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -865,7 +867,7 @@ impl Token {
|
|||
_ => return None,
|
||||
},
|
||||
SingleQuote => match joint.kind {
|
||||
Ident(name, IdentIsRaw::No) => Lifetime(Symbol::intern(&format!("'{name}"))),
|
||||
Ident(name, is_raw) => Lifetime(Symbol::intern(&format!("'{name}")), is_raw),
|
||||
_ => return None,
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -482,11 +482,11 @@ impl TokenStream {
|
|||
token::NtIdent(ident, is_raw) => {
|
||||
TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing)
|
||||
}
|
||||
token::NtLifetime(ident) => TokenTree::Delimited(
|
||||
token::NtLifetime(ident, is_raw) => TokenTree::Delimited(
|
||||
DelimSpan::from_single(token.span),
|
||||
DelimSpacing::new(Spacing::JointHidden, spacing),
|
||||
Delimiter::Invisible,
|
||||
TokenStream::token_alone(token::Lifetime(ident.name), ident.span),
|
||||
TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span),
|
||||
),
|
||||
token::Interpolated(ref nt) => TokenTree::Delimited(
|
||||
DelimSpan::from_single(token.span),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ use std::borrow::Cow;
|
|||
use ast::TraitBoundModifiers;
|
||||
use rustc_ast::attr::AttrIdGenerator;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
|
||||
use rustc_ast::token::{
|
||||
self, BinOpToken, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind,
|
||||
};
|
||||
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::comments::{Comment, CommentStyle};
|
||||
|
|
@ -947,8 +949,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
token::NtIdent(ident, is_raw) => {
|
||||
IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
|
||||
}
|
||||
token::Lifetime(name) => name.to_string().into(),
|
||||
token::NtLifetime(ident) => ident.name.to_string().into(),
|
||||
|
||||
token::Lifetime(name, IdentIsRaw::No)
|
||||
| token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(),
|
||||
token::Lifetime(name, IdentIsRaw::Yes)
|
||||
| token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => {
|
||||
format!("'r#{}", &name.as_str()[1..]).into()
|
||||
}
|
||||
|
||||
/* Other */
|
||||
token::DocComment(comment_kind, attr_style, data) => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_middle::span_bug;
|
|||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
|
||||
impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'infcx> {
|
||||
self.infcx.dcx()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,24 +15,24 @@ use tracing::debug;
|
|||
use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext};
|
||||
|
||||
/// The results of the dataflow analyses used by the borrow checker.
|
||||
pub(crate) struct BorrowckResults<'a, 'mir, 'tcx> {
|
||||
pub(crate) borrows: Results<'tcx, Borrows<'a, 'mir, 'tcx>>,
|
||||
pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'a, 'mir, 'tcx>>,
|
||||
pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'a, 'mir, 'tcx>>,
|
||||
pub(crate) struct BorrowckResults<'a, 'tcx> {
|
||||
pub(crate) borrows: Results<'tcx, Borrows<'a, 'tcx>>,
|
||||
pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'a, 'tcx>>,
|
||||
pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'a, 'tcx>>,
|
||||
}
|
||||
|
||||
/// The transient state of the dataflow analyses used by the borrow checker.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct BorrowckFlowState<'a, 'mir, 'tcx> {
|
||||
pub(crate) borrows: <Borrows<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) struct BorrowckFlowState<'a, 'tcx> {
|
||||
pub(crate) borrows: <Borrows<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'mir, 'tcx> {
|
||||
impl<'a, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'tcx> {
|
||||
// All three analyses are forward, but we have to use just one here.
|
||||
type Direction = <Borrows<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Direction;
|
||||
type FlowState = BorrowckFlowState<'a, 'mir, 'tcx>;
|
||||
type Direction = <Borrows<'a, 'tcx> as AnalysisDomain<'tcx>>::Direction;
|
||||
type FlowState = BorrowckFlowState<'a, 'tcx>;
|
||||
|
||||
fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
|
||||
BorrowckFlowState {
|
||||
|
|
@ -106,10 +106,9 @@ rustc_index::newtype_index! {
|
|||
/// `BorrowIndex`, and maps each such index to a `BorrowData`
|
||||
/// describing the borrow. These indexes are used for representing the
|
||||
/// borrows in compact bitvectors.
|
||||
pub struct Borrows<'a, 'mir, 'tcx> {
|
||||
pub struct Borrows<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
|
||||
body: &'a Body<'tcx>,
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
|
||||
}
|
||||
|
|
@ -389,10 +388,10 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx> Borrows<'a, 'mir, 'tcx> {
|
||||
impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
||||
pub fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
body: &'a Body<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
) -> Self {
|
||||
|
|
@ -494,7 +493,7 @@ impl<'a, 'mir, 'tcx> Borrows<'a, 'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, '_, 'tcx> {
|
||||
impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
|
||||
type Domain = BitSet<BorrowIndex>;
|
||||
|
||||
const NAME: &'static str = "borrows";
|
||||
|
|
@ -517,7 +516,7 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, '_, 'tcx> {
|
|||
/// region stops containing the CFG points reachable from the issuing location.
|
||||
/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
|
||||
/// `a.b.c` when `a` is overwritten.
|
||||
impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, '_, 'tcx> {
|
||||
impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
||||
type Idx = BorrowIndex;
|
||||
|
||||
fn domain_size(&self, _: &mir::Body<'tcx>) -> usize {
|
||||
|
|
@ -617,8 +616,8 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, '_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl DebugWithContext<Borrows<'_, '_, '_>> for BorrowIndex {
|
||||
fn fmt_with(&self, ctxt: &Borrows<'_, '_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
impl DebugWithContext<Borrows<'_, '_>> for BorrowIndex {
|
||||
fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", ctxt.location(*self))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ impl<'tcx> UniverseInfo<'tcx> {
|
|||
|
||||
pub(crate) fn report_error(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
|
||||
placeholder: ty::PlaceholderRegion,
|
||||
error_element: RegionElement,
|
||||
cause: ObligationCause<'tcx>,
|
||||
|
|
@ -151,7 +151,7 @@ trait TypeOpInfo<'tcx> {
|
|||
|
||||
fn nice_error<'infcx>(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
placeholder_region: ty::Region<'tcx>,
|
||||
error_region: Option<ty::Region<'tcx>>,
|
||||
|
|
@ -160,7 +160,7 @@ trait TypeOpInfo<'tcx> {
|
|||
#[instrument(level = "debug", skip(self, mbcx))]
|
||||
fn report_error(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
|
||||
placeholder: ty::PlaceholderRegion,
|
||||
error_element: RegionElement,
|
||||
cause: ObligationCause<'tcx>,
|
||||
|
|
@ -233,7 +233,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
|
|||
|
||||
fn nice_error<'infcx>(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
placeholder_region: ty::Region<'tcx>,
|
||||
error_region: Option<ty::Region<'tcx>>,
|
||||
|
|
@ -277,7 +277,7 @@ where
|
|||
|
||||
fn nice_error<'infcx>(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
placeholder_region: ty::Region<'tcx>,
|
||||
error_region: Option<ty::Region<'tcx>>,
|
||||
|
|
@ -324,7 +324,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
|
|||
|
||||
fn nice_error<'infcx>(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
|
||||
cause: ObligationCause<'tcx>,
|
||||
placeholder_region: ty::Region<'tcx>,
|
||||
error_region: Option<ty::Region<'tcx>>,
|
||||
|
|
@ -357,7 +357,7 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
|
|||
|
||||
fn nice_error<'infcx>(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
|
||||
_cause: ObligationCause<'tcx>,
|
||||
placeholder_region: ty::Region<'tcx>,
|
||||
error_region: Option<ty::Region<'tcx>>,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ enum StorageDeadOrDrop<'tcx> {
|
|||
Destructor(Ty<'tcx>),
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn report_use_of_moved_or_uninitialized(
|
||||
&mut self,
|
||||
location: Location,
|
||||
|
|
@ -4358,11 +4358,7 @@ enum AnnotatedBorrowFnSignature<'tcx> {
|
|||
impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
|
||||
/// Annotate the provided diagnostic with information about borrow from the fn signature that
|
||||
/// helps explain.
|
||||
pub(crate) fn emit(
|
||||
&self,
|
||||
cx: &MirBorrowckCtxt<'_, '_, '_, 'tcx>,
|
||||
diag: &mut Diag<'_>,
|
||||
) -> String {
|
||||
pub(crate) fn emit(&self, cx: &MirBorrowckCtxt<'_, '_, 'tcx>, diag: &mut Diag<'_>) -> String {
|
||||
match self {
|
||||
&AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
|
||||
diag.span_label(
|
||||
|
|
|
|||
|
|
@ -390,7 +390,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
||||
fn free_region_constraint_info(
|
||||
&self,
|
||||
borrow_region: RegionVid,
|
||||
|
|
@ -662,9 +662,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
|||
// `&dyn Trait`
|
||||
ty::Ref(_, ty, _) if ty.is_trait() => true,
|
||||
// `Box<dyn Trait>`
|
||||
_ if ty.is_box() && ty.boxed_ty().is_trait() => {
|
||||
_ if ty.boxed_ty().is_some_and(Ty::is_trait) => {
|
||||
true
|
||||
}
|
||||
|
||||
// `dyn Trait`
|
||||
_ if ty.is_trait() => true,
|
||||
// Anything else.
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ pub(super) struct DescribePlaceOpt {
|
|||
|
||||
pub(super) struct IncludingTupleField(pub(super) bool);
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
/// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
|
||||
/// is moved after being invoked.
|
||||
///
|
||||
|
|
@ -345,9 +345,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
|||
variant_index: Option<VariantIdx>,
|
||||
including_tuple_field: IncludingTupleField,
|
||||
) -> Option<String> {
|
||||
if ty.is_box() {
|
||||
if let Some(boxed_ty) = ty.boxed_ty() {
|
||||
// If the type is a box, the field is described from the boxed type
|
||||
self.describe_field_from_ty(ty.boxed_ty(), field, variant_index, including_tuple_field)
|
||||
self.describe_field_from_ty(boxed_ty, field, variant_index, including_tuple_field)
|
||||
} else {
|
||||
match *ty.kind() {
|
||||
ty::Adt(def, _) => {
|
||||
|
|
@ -772,7 +772,7 @@ struct CapturedMessageOpt {
|
|||
maybe_reinitialized_locations_is_empty: bool,
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
/// Finds the spans associated to a move or copy of move_place at location.
|
||||
pub(super) fn move_spans(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ enum GroupedMoveError<'tcx> {
|
|||
},
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn report_move_errors(&mut self) {
|
||||
let grouped_errors = self.group_move_errors();
|
||||
for error in grouped_errors {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ pub(crate) enum AccessKind {
|
|||
Mutate,
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn report_mutability_error(
|
||||
&mut self,
|
||||
access_place: Place<'tcx>,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ impl OutlivesSuggestionBuilder {
|
|||
/// Returns a name for the region if it is suggestable. See `region_name_is_suggestable`.
|
||||
fn region_vid_to_name(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_, '_, '_>,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_, '_>,
|
||||
region: RegionVid,
|
||||
) -> Option<RegionName> {
|
||||
mbcx.give_region_a_name(region).filter(Self::region_name_is_suggestable)
|
||||
|
|
@ -85,7 +85,7 @@ impl OutlivesSuggestionBuilder {
|
|||
/// Compiles a list of all suggestions to be printed in the final big suggestion.
|
||||
fn compile_all_suggestions(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_, '_, '_>,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_, '_>,
|
||||
) -> SmallVec<[SuggestedConstraint; 2]> {
|
||||
let mut suggested = SmallVec::new();
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ impl OutlivesSuggestionBuilder {
|
|||
/// Emit an intermediate note on the given `Diag` if the involved regions are suggestable.
|
||||
pub(crate) fn intermediate_suggestion(
|
||||
&mut self,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_, '_, '_>,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_, '_>,
|
||||
errci: &ErrorConstraintInfo<'_>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
|
|
@ -180,7 +180,7 @@ impl OutlivesSuggestionBuilder {
|
|||
|
||||
/// If there is a suggestion to emit, add a diagnostic to the buffer. This is the final
|
||||
/// suggestion including all collected constraints.
|
||||
pub(crate) fn add_suggestion(&self, mbcx: &mut MirBorrowckCtxt<'_, '_, '_, '_>) {
|
||||
pub(crate) fn add_suggestion(&self, mbcx: &mut MirBorrowckCtxt<'_, '_, '_>) {
|
||||
// No constraints to add? Done.
|
||||
if self.constraints_to_add.is_empty() {
|
||||
debug!("No constraints to suggest.");
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ pub(crate) struct ErrorConstraintInfo<'tcx> {
|
|||
pub(super) span: Span,
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
/// Converts a region inference variable into a `ty::Region` that
|
||||
/// we can use for error reporting. If `r` is universally bound,
|
||||
/// then we use the name that we have on record for it. If `r` is
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ impl rustc_errors::IntoDiagArg for RegionName {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
||||
pub(crate) fn mir_def_id(&self) -> hir::def_id::LocalDefId {
|
||||
self.body.source.def_id().expect_local()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -304,11 +304,11 @@ fn do_mir_borrowck<'tcx>(
|
|||
promoted_mbcx.report_move_errors();
|
||||
diags = promoted_mbcx.diags;
|
||||
|
||||
struct MoveVisitor<'a, 'b, 'mir, 'infcx, 'tcx> {
|
||||
ctxt: &'a mut MirBorrowckCtxt<'b, 'mir, 'infcx, 'tcx>,
|
||||
struct MoveVisitor<'a, 'b, 'infcx, 'tcx> {
|
||||
ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, '_, '_, 'tcx> {
|
||||
impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx> {
|
||||
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||
if let Operand::Move(place) = operand {
|
||||
self.ctxt.check_movable_place(location, *place);
|
||||
|
|
@ -522,10 +522,10 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx> {
|
||||
struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
|
||||
infcx: &'infcx BorrowckInferCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
body: &'a Body<'tcx>,
|
||||
move_data: &'a MoveData<'tcx>,
|
||||
|
||||
/// Map from MIR `Location` to `LocationIndex`; created
|
||||
|
|
@ -599,16 +599,16 @@ struct MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx> {
|
|||
// 2. loans made in overlapping scopes do not conflict
|
||||
// 3. assignments do not affect things loaned out as immutable
|
||||
// 4. moves do not affect things loaned out in any way
|
||||
impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
|
||||
for MirBorrowckCtxt<'a, 'mir, '_, 'tcx>
|
||||
impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R>
|
||||
for MirBorrowckCtxt<'a, '_, 'tcx>
|
||||
{
|
||||
type FlowState = Flows<'a, 'mir, 'tcx>;
|
||||
type FlowState = Flows<'a, 'tcx>;
|
||||
|
||||
fn visit_statement_before_primary_effect(
|
||||
&mut self,
|
||||
_results: &mut R,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
stmt: &'mir Statement<'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
stmt: &'a Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, flow_state);
|
||||
|
|
@ -677,8 +677,8 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
|
|||
fn visit_terminator_before_primary_effect(
|
||||
&mut self,
|
||||
_results: &mut R,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
term: &'mir Terminator<'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
term: &'a Terminator<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, flow_state);
|
||||
|
|
@ -794,8 +794,8 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
|
|||
fn visit_terminator_after_primary_effect(
|
||||
&mut self,
|
||||
_results: &mut R,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
term: &'mir Terminator<'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
term: &'a Terminator<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
let span = term.source_info.span;
|
||||
|
|
@ -972,8 +972,8 @@ impl InitializationRequiringAction {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
||||
fn body(&self) -> &'mir Body<'tcx> {
|
||||
impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
||||
fn body(&self) -> &'a Body<'tcx> {
|
||||
self.body
|
||||
}
|
||||
|
||||
|
|
@ -989,7 +989,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
place_span: (Place<'tcx>, Span),
|
||||
kind: (AccessDepth, ReadOrWrite),
|
||||
is_local_mutation_allowed: LocalMutationIsAllowed,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) {
|
||||
let (sd, rw) = kind;
|
||||
|
||||
|
|
@ -1039,7 +1039,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
place_span: (Place<'tcx>, Span),
|
||||
sd: AccessDepth,
|
||||
rw: ReadOrWrite,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) -> bool {
|
||||
let mut error_reported = false;
|
||||
let borrow_set = Rc::clone(&self.borrow_set);
|
||||
|
|
@ -1180,7 +1180,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
location: Location,
|
||||
place_span: (Place<'tcx>, Span),
|
||||
kind: AccessDepth,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) {
|
||||
// Write of P[i] or *P requires P init'd.
|
||||
self.check_if_assigned_path_is_moved(location, place_span, flow_state);
|
||||
|
|
@ -1197,8 +1197,8 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
fn consume_rvalue(
|
||||
&mut self,
|
||||
location: Location,
|
||||
(rvalue, span): (&'mir Rvalue<'tcx>, Span),
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
(rvalue, span): (&'a Rvalue<'tcx>, Span),
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) {
|
||||
match rvalue {
|
||||
&Rvalue::Ref(_ /*rgn*/, bk, place) => {
|
||||
|
|
@ -1455,8 +1455,8 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
fn consume_operand(
|
||||
&mut self,
|
||||
location: Location,
|
||||
(operand, span): (&'mir Operand<'tcx>, Span),
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
(operand, span): (&'a Operand<'tcx>, Span),
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) {
|
||||
match *operand {
|
||||
Operand::Copy(place) => {
|
||||
|
|
@ -1576,12 +1576,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_activations(
|
||||
&mut self,
|
||||
location: Location,
|
||||
span: Span,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
) {
|
||||
fn check_activations(&mut self, location: Location, span: Span, flow_state: &Flows<'a, 'tcx>) {
|
||||
// Two-phase borrow support: For each activation that is newly
|
||||
// generated at this statement, check if it interferes with
|
||||
// another borrow.
|
||||
|
|
@ -1744,7 +1739,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
location: Location,
|
||||
desired_action: InitializationRequiringAction,
|
||||
place_span: (PlaceRef<'tcx>, Span),
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) {
|
||||
let maybe_uninits = &flow_state.uninits;
|
||||
|
||||
|
|
@ -1849,7 +1844,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
location: Location,
|
||||
desired_action: InitializationRequiringAction,
|
||||
place_span: (PlaceRef<'tcx>, Span),
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) {
|
||||
let maybe_uninits = &flow_state.uninits;
|
||||
|
||||
|
|
@ -1948,7 +1943,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
&mut self,
|
||||
location: Location,
|
||||
(place, span): (Place<'tcx>, Span),
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) {
|
||||
debug!("check_if_assigned_path_is_moved place: {:?}", place);
|
||||
|
||||
|
|
@ -2009,12 +2004,12 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_parent_of_field<'mir, 'tcx>(
|
||||
this: &mut MirBorrowckCtxt<'_, 'mir, '_, 'tcx>,
|
||||
fn check_parent_of_field<'a, 'tcx>(
|
||||
this: &mut MirBorrowckCtxt<'a, '_, 'tcx>,
|
||||
location: Location,
|
||||
base: PlaceRef<'tcx>,
|
||||
span: Span,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) {
|
||||
// rust-lang/rust#21232: Until Rust allows reads from the
|
||||
// initialized parts of partially initialized structs, we
|
||||
|
|
@ -2105,7 +2100,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
(place, span): (Place<'tcx>, Span),
|
||||
kind: ReadOrWrite,
|
||||
is_local_mutation_allowed: LocalMutationIsAllowed,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
location: Location,
|
||||
) -> bool {
|
||||
debug!(
|
||||
|
|
@ -2221,7 +2216,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
fn is_local_ever_initialized(
|
||||
&self,
|
||||
local: Local,
|
||||
flow_state: &Flows<'_, 'mir, 'tcx>,
|
||||
flow_state: &Flows<'a, 'tcx>,
|
||||
) -> Option<InitIndex> {
|
||||
let mpi = self.move_data.rev_lookup.find_local(local)?;
|
||||
let ii = &self.move_data.init_path_map[mpi];
|
||||
|
|
@ -2229,7 +2224,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
|
|||
}
|
||||
|
||||
/// Adds the place into the used mutable variables set
|
||||
fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'_, 'mir, 'tcx>) {
|
||||
fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'a, 'tcx>) {
|
||||
match root_place {
|
||||
RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => {
|
||||
// If the local may have been initialized, and it is now currently being
|
||||
|
|
@ -2484,7 +2479,7 @@ mod diags {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
|
||||
self.diags.buffer_error(diag);
|
||||
}
|
||||
|
|
@ -2522,7 +2517,7 @@ mod diags {
|
|||
}
|
||||
|
||||
pub(crate) fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
|
||||
let mut res = None;
|
||||
let mut res = self.infcx.tainted_by_errors();
|
||||
|
||||
// Buffer any move errors that we collected and de-duplicated.
|
||||
for (_, (_, diag)) in std::mem::take(&mut self.diags.buffered_move_errors) {
|
||||
|
|
|
|||
|
|
@ -75,14 +75,14 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
|
|||
/// Computes the (non-lexical) regions from the input MIR.
|
||||
///
|
||||
/// This may result in errors being reported.
|
||||
pub(crate) fn compute_regions<'cx, 'tcx>(
|
||||
pub(crate) fn compute_regions<'a, 'tcx>(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
universal_regions: UniversalRegions<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
promoted: &IndexSlice<Promoted, Body<'tcx>>,
|
||||
location_table: &LocationTable,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'_, 'cx, 'tcx>>,
|
||||
flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
upvars: &[&ty::CapturedPlace<'tcx>],
|
||||
|
|
@ -301,13 +301,13 @@ pub(super) fn dump_nll_mir<'tcx>(
|
|||
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
pub(super) fn dump_annotation<'tcx, 'cx>(
|
||||
infcx: &'cx BorrowckInferCtxt<'tcx>,
|
||||
pub(super) fn dump_annotation<'tcx, 'infcx>(
|
||||
infcx: &'infcx BorrowckInferCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
|
||||
diags: &mut crate::diags::BorrowckDiags<'cx, 'tcx>,
|
||||
diags: &mut crate::diags::BorrowckDiags<'infcx, 'tcx>,
|
||||
) {
|
||||
let tcx = infcx.tcx;
|
||||
let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ pub(super) enum PrefixSet {
|
|||
Shallow,
|
||||
}
|
||||
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
||||
/// Returns an iterator over the prefixes of `place`
|
||||
/// (inclusive) from longest to smallest, potentially
|
||||
/// terminating the iteration early based on `kind`.
|
||||
|
|
|
|||
|
|
@ -30,11 +30,11 @@ mod trace;
|
|||
///
|
||||
/// N.B., this computation requires normalization; therefore, it must be
|
||||
/// performed before
|
||||
pub(super) fn generate<'mir, 'tcx>(
|
||||
pub(super) fn generate<'a, 'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
elements: &Rc<DenseLocationMap>,
|
||||
flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>,
|
||||
flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
) {
|
||||
debug!("liveness::generate");
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ use crate::type_check::{NormalizeLocation, TypeChecker};
|
|||
/// DROP-LIVE set are to the liveness sets for regions found in the
|
||||
/// `dropck_outlives` result of the variable's type (in particular,
|
||||
/// this respects `#[may_dangle]` annotations).
|
||||
pub(super) fn trace<'mir, 'tcx>(
|
||||
pub(super) fn trace<'a, 'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
elements: &Rc<DenseLocationMap>,
|
||||
flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>,
|
||||
flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
relevant_live_locals: Vec<Local>,
|
||||
boring_locals: Vec<Local>,
|
||||
|
|
@ -99,29 +99,29 @@ pub(super) fn trace<'mir, 'tcx>(
|
|||
}
|
||||
|
||||
/// Contextual state for the type-liveness coroutine.
|
||||
struct LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx> {
|
||||
struct LivenessContext<'a, 'typeck, 'b, 'tcx> {
|
||||
/// Current type-checker, giving us our inference context etc.
|
||||
typeck: &'me mut TypeChecker<'typeck, 'tcx>,
|
||||
typeck: &'a mut TypeChecker<'typeck, 'tcx>,
|
||||
|
||||
/// Defines the `PointIndex` mapping
|
||||
elements: &'me DenseLocationMap,
|
||||
elements: &'a DenseLocationMap,
|
||||
|
||||
/// MIR we are analyzing.
|
||||
body: &'me Body<'tcx>,
|
||||
body: &'a Body<'tcx>,
|
||||
|
||||
/// Mapping to/from the various indices used for initialization tracking.
|
||||
move_data: &'me MoveData<'tcx>,
|
||||
move_data: &'a MoveData<'tcx>,
|
||||
|
||||
/// Cache for the results of `dropck_outlives` query.
|
||||
drop_data: FxIndexMap<Ty<'tcx>, DropData<'tcx>>,
|
||||
|
||||
/// Results of dataflow tracking which variables (and paths) have been
|
||||
/// initialized.
|
||||
flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'a, 'flow, 'tcx>>,
|
||||
flow_inits: &'a mut ResultsCursor<'b, 'tcx, MaybeInitializedPlaces<'b, 'tcx>>,
|
||||
|
||||
/// Index indicating where each variable is assigned, used, or
|
||||
/// dropped.
|
||||
local_use_map: &'me LocalUseMap,
|
||||
local_use_map: &'a LocalUseMap,
|
||||
}
|
||||
|
||||
struct DropData<'tcx> {
|
||||
|
|
@ -129,8 +129,8 @@ struct DropData<'tcx> {
|
|||
region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>,
|
||||
}
|
||||
|
||||
struct LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> {
|
||||
cx: LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx>,
|
||||
struct LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
||||
cx: LivenessContext<'a, 'typeck, 'b, 'tcx>,
|
||||
|
||||
/// Set of points that define the current local.
|
||||
defs: BitSet<PointIndex>,
|
||||
|
|
@ -151,8 +151,8 @@ struct LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> {
|
|||
stack: Vec<PointIndex>,
|
||||
}
|
||||
|
||||
impl<'a, 'me, 'typeck, 'flow, 'tcx> LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> {
|
||||
fn new(cx: LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx>) -> Self {
|
||||
impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
||||
fn new(cx: LivenessContext<'a, 'typeck, 'b, 'tcx>) -> Self {
|
||||
let num_points = cx.elements.num_points();
|
||||
LivenessResults {
|
||||
cx,
|
||||
|
|
@ -505,7 +505,7 @@ impl<'a, 'me, 'typeck, 'flow, 'tcx> LivenessResults<'a, 'me, 'typeck, 'flow, 'tc
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LivenessContext<'_, '_, '_, '_, 'tcx> {
|
||||
impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
||||
/// Returns `true` if the local variable (or some part of it) is initialized at the current
|
||||
/// cursor position. Callers should call one of the `seek` methods immediately before to point
|
||||
/// the cursor to the desired location.
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ mod relate_tys;
|
|||
/// - `flow_inits` -- results of a maybe-init dataflow analysis
|
||||
/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
|
||||
/// - `elements` -- MIR region map
|
||||
pub(crate) fn type_check<'mir, 'tcx>(
|
||||
pub(crate) fn type_check<'a, 'tcx>(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
|
|
@ -125,7 +125,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
|||
location_table: &LocationTable,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>,
|
||||
flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
elements: &Rc<DenseLocationMap>,
|
||||
upvars: &[&ty::CapturedPlace<'tcx>],
|
||||
|
|
@ -1979,19 +1979,76 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
|
||||
match cast_kind {
|
||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
|
||||
let fn_sig = op.ty(body, tcx).fn_sig(tcx);
|
||||
let src_sig = op.ty(body, tcx).fn_sig(tcx);
|
||||
|
||||
// HACK: This shouldn't be necessary... We can remove this when we actually
|
||||
// get binders with where clauses, then elaborate implied bounds into that
|
||||
// binder, and implement a higher-ranked subtyping algorithm that actually
|
||||
// respects these implied bounds.
|
||||
//
|
||||
// This protects against the case where we are casting from a higher-ranked
|
||||
// fn item to a non-higher-ranked fn pointer, where the cast throws away
|
||||
// implied bounds that would've needed to be checked at the call site. This
|
||||
// only works when we're casting to a non-higher-ranked fn ptr, since
|
||||
// placeholders in the target signature could have untracked implied
|
||||
// bounds, resulting in incorrect errors.
|
||||
//
|
||||
// We check that this signature is WF before subtyping the signature with
|
||||
// the target fn sig.
|
||||
if src_sig.has_bound_regions()
|
||||
&& let ty::FnPtr(target_fn_tys, target_hdr) = *ty.kind()
|
||||
&& let target_sig = target_fn_tys.with(target_hdr)
|
||||
&& let Some(target_sig) = target_sig.no_bound_vars()
|
||||
{
|
||||
let src_sig = self.infcx.instantiate_binder_with_fresh_vars(
|
||||
span,
|
||||
BoundRegionConversionTime::HigherRankedType,
|
||||
src_sig,
|
||||
);
|
||||
let src_ty = Ty::new_fn_ptr(self.tcx(), ty::Binder::dummy(src_sig));
|
||||
self.prove_predicate(
|
||||
ty::ClauseKind::WellFormed(src_ty.into()),
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
);
|
||||
|
||||
let src_ty = self.normalize(src_ty, location);
|
||||
if let Err(terr) = self.sub_types(
|
||||
src_ty,
|
||||
*ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
rvalue,
|
||||
"equating {:?} with {:?} yields {:?}",
|
||||
target_sig,
|
||||
src_sig,
|
||||
terr
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
let src_ty = Ty::new_fn_ptr(tcx, src_sig);
|
||||
// HACK: We want to assert that the signature of the source fn is
|
||||
// well-formed, because we don't enforce that via the WF of FnDef
|
||||
// types normally. This should be removed when we improve the tracking
|
||||
// of implied bounds of fn signatures.
|
||||
self.prove_predicate(
|
||||
ty::ClauseKind::WellFormed(src_ty.into()),
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
);
|
||||
|
||||
// The type that we see in the fcx is like
|
||||
// `foo::<'a, 'b>`, where `foo` is the path to a
|
||||
// function definition. When we extract the
|
||||
// signature, it comes from the `fn_sig` query,
|
||||
// and hence may contain unnormalized results.
|
||||
let fn_sig = self.normalize(fn_sig, location);
|
||||
|
||||
let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);
|
||||
|
||||
let src_ty = self.normalize(src_ty, location);
|
||||
if let Err(terr) = self.sub_types(
|
||||
ty_fn_ptr_from,
|
||||
src_ty,
|
||||
*ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
|
|
@ -2000,7 +2057,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
self,
|
||||
rvalue,
|
||||
"equating {:?} with {:?} yields {:?}",
|
||||
ty_fn_ptr_from,
|
||||
src_ty,
|
||||
ty,
|
||||
terr
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ use rustc_macros::extension;
|
|||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{
|
||||
self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, TyCtxt,
|
||||
self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty,
|
||||
TyCtxt, TypeVisitableExt,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
|
|
@ -422,8 +423,8 @@ impl<'tcx> UniversalRegions<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct UniversalRegionsBuilder<'cx, 'tcx> {
|
||||
infcx: &'cx BorrowckInferCtxt<'tcx>,
|
||||
struct UniversalRegionsBuilder<'infcx, 'tcx> {
|
||||
infcx: &'infcx BorrowckInferCtxt<'tcx>,
|
||||
mir_def: LocalDefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
}
|
||||
|
|
@ -688,7 +689,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
defining_ty: DefiningTy<'tcx>,
|
||||
) -> ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>> {
|
||||
let tcx = self.infcx.tcx;
|
||||
match defining_ty {
|
||||
|
||||
let inputs_and_output = match defining_ty {
|
||||
DefiningTy::Closure(def_id, args) => {
|
||||
assert_eq!(self.mir_def.to_def_id(), def_id);
|
||||
let closure_sig = args.as_closure().sig();
|
||||
|
|
@ -798,6 +800,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
// "output" (the type of the constant).
|
||||
assert_eq!(self.mir_def.to_def_id(), def_id);
|
||||
let ty = tcx.type_of(self.mir_def).instantiate_identity();
|
||||
|
||||
let ty = indices.fold_to_region_vids(tcx, ty);
|
||||
ty::Binder::dummy(tcx.mk_type_list(&[ty]))
|
||||
}
|
||||
|
|
@ -807,7 +810,14 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
let ty = args.as_inline_const().ty();
|
||||
ty::Binder::dummy(tcx.mk_type_list(&[ty]))
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME(#129952): We probably want a more principled approach here.
|
||||
if let Err(terr) = inputs_and_output.skip_binder().error_reported() {
|
||||
self.infcx.set_tainted_by_errors(terr);
|
||||
}
|
||||
|
||||
inputs_and_output
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use tracing::debug;
|
|||
|
||||
use crate::MirBorrowckCtxt;
|
||||
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
||||
/// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
|
||||
/// of the `unused_mut` lint.
|
||||
///
|
||||
|
|
@ -46,13 +46,13 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
|||
|
||||
/// MIR visitor for collecting used mutable variables.
|
||||
/// The 'visit lifetime represents the duration of the MIR walk.
|
||||
struct GatherUsedMutsVisitor<'visit, 'a, 'mir, 'infcx, 'tcx> {
|
||||
struct GatherUsedMutsVisitor<'a, 'b, 'infcx, 'tcx> {
|
||||
temporary_used_locals: FxIndexSet<Local>,
|
||||
never_initialized_mut_locals: &'visit mut FxIndexSet<Local>,
|
||||
mbcx: &'visit mut MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx>,
|
||||
never_initialized_mut_locals: &'a mut FxIndexSet<Local>,
|
||||
mbcx: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>,
|
||||
}
|
||||
|
||||
impl GatherUsedMutsVisitor<'_, '_, '_, '_, '_> {
|
||||
impl GatherUsedMutsVisitor<'_, '_, '_, '_> {
|
||||
fn remove_never_initialized_mut_locals(&mut self, into: Place<'_>) {
|
||||
// Remove any locals that we found were initialized from the
|
||||
// `never_initialized_mut_locals` set. At the end, the only remaining locals will
|
||||
|
|
@ -64,7 +64,7 @@ impl GatherUsedMutsVisitor<'_, '_, '_, '_, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'_, '_, '_, '_, 'tcx> {
|
||||
impl<'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'_, '_, '_, 'tcx> {
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
debug!("visit_terminator: terminator={:?}", terminator);
|
||||
match &terminator.kind {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ For more docs on how to build and test see [build_system/usage.txt](build_system
|
|||
|AIX|❌[^xcoff]|N/A|N/A|❌[^xcoff]|
|
||||
|Other unixes|❓|❓|❓|❓|
|
||||
|macOS|✅|✅|N/A|N/A|
|
||||
|Windows|✅[^no-rustup]|❌|N/A|N/A|
|
||||
|Windows|✅|❌|N/A|N/A|
|
||||
|
||||
✅: Fully supported and tested
|
||||
❓: Maybe supported, not tested
|
||||
|
|
|
|||
|
|
@ -11,63 +11,12 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
|
||||
[[package]]
|
||||
name = "boml"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85fdb93f04c73bff54305fa437ffea5449c41edcaadfe882f35836206b166ac5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
|
||||
[[package]]
|
||||
name = "fm"
|
||||
version = "0.2.2"
|
||||
|
|
@ -132,12 +81,6 @@ version = "0.2.150"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
|
|
@ -154,24 +97,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.30.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.4"
|
||||
|
|
@ -196,22 +121,6 @@ dependencies = [
|
|||
"boml",
|
||||
"gccjit",
|
||||
"lang_tester",
|
||||
"object",
|
||||
"smallvec",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -223,25 +132,6 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
|
|
@ -315,69 +205,3 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
|
|
|||
|
|
@ -23,21 +23,11 @@ default = ["master"]
|
|||
|
||||
[dependencies]
|
||||
gccjit = "2.1"
|
||||
|
||||
# Local copy.
|
||||
#gccjit = { path = "../gccjit.rs" }
|
||||
|
||||
object = { version = "0.30.1", default-features = false, features = [
|
||||
"std",
|
||||
"read",
|
||||
] }
|
||||
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
|
||||
# TODO(antoyo): make tempfile optional.
|
||||
tempfile = "3.7.1"
|
||||
|
||||
[dev-dependencies]
|
||||
lang_tester = "0.8.0"
|
||||
tempfile = "3.1.0"
|
||||
boml = "0.3.1"
|
||||
|
||||
[profile.dev]
|
||||
|
|
|
|||
|
|
@ -24,6 +24,14 @@
|
|||
#![deny(clippy::pattern_type_mismatch)]
|
||||
#![allow(clippy::needless_lifetimes)]
|
||||
|
||||
// Some "regular" crates we want to share with rustc
|
||||
extern crate object;
|
||||
extern crate smallvec;
|
||||
extern crate tempfile;
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
// The rustc crates we need
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_attr;
|
||||
|
|
@ -42,8 +50,6 @@ extern crate rustc_middle;
|
|||
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)]
|
||||
|
|
|
|||
|
|
@ -411,26 +411,31 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
// the string "false". Now it is disabled by absence of the attribute.
|
||||
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
|
||||
}
|
||||
} else if llvm_util::get_version() >= (19, 0, 0) {
|
||||
// For non-naked functions, set branch protection attributes on aarch64.
|
||||
if let Some(BranchProtection { bti, pac_ret }) =
|
||||
cx.sess().opts.unstable_opts.branch_protection
|
||||
{
|
||||
assert!(cx.sess().target.arch == "aarch64");
|
||||
if bti {
|
||||
to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
|
||||
}
|
||||
if let Some(PacRet { leaf, key }) = pac_ret {
|
||||
to_add.push(llvm::CreateAttrStringValue(
|
||||
cx.llcx,
|
||||
"sign-return-address",
|
||||
if leaf { "all" } else { "non-leaf" },
|
||||
));
|
||||
to_add.push(llvm::CreateAttrStringValue(
|
||||
cx.llcx,
|
||||
"sign-return-address-key",
|
||||
if key == PAuthKey::A { "a_key" } else { "b_key" },
|
||||
));
|
||||
} else {
|
||||
// Do not set sanitizer attributes for naked functions.
|
||||
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
|
||||
|
||||
if llvm_util::get_version() >= (19, 0, 0) {
|
||||
// For non-naked functions, set branch protection attributes on aarch64.
|
||||
if let Some(BranchProtection { bti, pac_ret }) =
|
||||
cx.sess().opts.unstable_opts.branch_protection
|
||||
{
|
||||
assert!(cx.sess().target.arch == "aarch64");
|
||||
if bti {
|
||||
to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
|
||||
}
|
||||
if let Some(PacRet { leaf, key }) = pac_ret {
|
||||
to_add.push(llvm::CreateAttrStringValue(
|
||||
cx.llcx,
|
||||
"sign-return-address",
|
||||
if leaf { "all" } else { "non-leaf" },
|
||||
));
|
||||
to_add.push(llvm::CreateAttrStringValue(
|
||||
cx.llcx,
|
||||
"sign-return-address-key",
|
||||
if key == PAuthKey::A { "a_key" } else { "b_key" },
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -485,7 +490,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
if let Some(backchain) = backchain_attr(cx) {
|
||||
to_add.push(backchain);
|
||||
}
|
||||
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
|
||||
to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry));
|
||||
|
||||
// Always annotate functions with the target-cpu they are compiled for.
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) ->
|
|||
if def.is_box()
|
||||
&& args.get(1).map_or(true, |arg| cx.layout_of(arg.expect_ty()).is_1zst()) =>
|
||||
{
|
||||
build_pointer_or_reference_di_node(cx, t, t.boxed_ty(), unique_type_id)
|
||||
build_pointer_or_reference_di_node(cx, t, t.expect_boxed_ty(), unique_type_id)
|
||||
}
|
||||
ty::FnDef(..) | ty::FnPtr(..) => build_subroutine_type_di_node(cx, unique_type_id),
|
||||
ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(bootstrap, feature(unsafe_extern_blocks))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
|
|
|
|||
|
|
@ -353,9 +353,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
|
|||
None
|
||||
}
|
||||
})
|
||||
.filter(|feature| {
|
||||
RUSTC_SPECIAL_FEATURES.contains(feature) || features.contains(&Symbol::intern(feature))
|
||||
})
|
||||
.filter(|feature| features.contains(&Symbol::intern(feature)))
|
||||
.map(|feature| Symbol::intern(feature))
|
||||
.collect()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -372,27 +372,42 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
|
|||
Some(file)
|
||||
}
|
||||
|
||||
/// Since Xcode 15 Apple's LD requires object files to contain information about what they were
|
||||
/// built for (LC_BUILD_VERSION): the platform (macOS/watchOS etc), minimum OS version, and SDK
|
||||
/// version. This returns a `MachOBuildVersion` for the target.
|
||||
/// Mach-O files contain information about:
|
||||
/// - The platform/OS they were built for (macOS/watchOS/Mac Catalyst/iOS simulator etc).
|
||||
/// - The minimum OS version / deployment target.
|
||||
/// - The version of the SDK they were targetting.
|
||||
///
|
||||
/// In the past, this was accomplished using the LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
|
||||
/// LC_VERSION_MIN_TVOS or LC_VERSION_MIN_WATCHOS load commands, which each contain information
|
||||
/// about the deployment target and SDK version, and implicitly, by their presence, which OS they
|
||||
/// target. Simulator targets were determined if the architecture was x86_64, but there was e.g. a
|
||||
/// LC_VERSION_MIN_IPHONEOS present.
|
||||
///
|
||||
/// This is of course brittle and limited, so modern tooling emit the LC_BUILD_VERSION load
|
||||
/// command (which contains all three pieces of information in one) when the deployment target is
|
||||
/// high enough, or the target is something that wouldn't be encodable with the old load commands
|
||||
/// (such as Mac Catalyst, or Aarch64 iOS simulator).
|
||||
///
|
||||
/// Since Xcode 15, Apple's LD apparently requires object files to use this load command, so this
|
||||
/// returns the `MachOBuildVersion` for the target to do so.
|
||||
fn macho_object_build_version_for_target(target: &Target) -> object::write::MachOBuildVersion {
|
||||
/// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
|
||||
/// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
|
||||
fn pack_version((major, minor): (u32, u32)) -> u32 {
|
||||
(major << 16) | (minor << 8)
|
||||
fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 {
|
||||
let (major, minor, patch) = (major as u32, minor as u32, patch as u32);
|
||||
(major << 16) | (minor << 8) | patch
|
||||
}
|
||||
|
||||
let platform =
|
||||
rustc_target::spec::current_apple_platform(target).expect("unknown Apple target OS");
|
||||
let min_os = rustc_target::spec::current_apple_deployment_target(target)
|
||||
.expect("unknown Apple target OS");
|
||||
let sdk =
|
||||
let min_os = rustc_target::spec::current_apple_deployment_target(target);
|
||||
let (sdk_major, sdk_minor) =
|
||||
rustc_target::spec::current_apple_sdk_version(platform).expect("unknown Apple target OS");
|
||||
|
||||
let mut build_version = object::write::MachOBuildVersion::default();
|
||||
build_version.platform = platform;
|
||||
build_version.minos = pack_version(min_os);
|
||||
build_version.sdk = pack_version(sdk);
|
||||
build_version.sdk = pack_version((sdk_major, sdk_minor, 0));
|
||||
build_version
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -538,8 +538,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
// final value.
|
||||
// Note: This is only sound if every local that has a `StorageDead` has a
|
||||
// `StorageDead` in every control flow path leading to a `return` terminator.
|
||||
// The good news is that interning will detect if any unexpected mutable
|
||||
// pointer slips through.
|
||||
// If anything slips through, there's no safety net -- safe code can create
|
||||
// references to variants of `!Freeze` enums as long as that variant is `Freeze`,
|
||||
// so interning can't protect us here.
|
||||
if self.local_is_transient(place.local) {
|
||||
self.check_op(ops::TransientCellBorrow);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use rustc_middle::traits::Reveal;
|
|||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::{self, Abi};
|
||||
|
|
@ -18,13 +17,12 @@ use tracing::{debug, instrument, trace};
|
|||
|
||||
use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine};
|
||||
use crate::const_eval::CheckAlignment;
|
||||
use crate::errors::{self, ConstEvalError, DanglingPtrInFinal};
|
||||
use crate::interpret::{
|
||||
create_static_alloc, eval_nullary_intrinsic, intern_const_alloc_recursive, throw_exhaust,
|
||||
CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpError,
|
||||
InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup,
|
||||
};
|
||||
use crate::CTRL_C_RECEIVED;
|
||||
use crate::{errors, CTRL_C_RECEIVED};
|
||||
|
||||
// Returns a pointer to where the result lives
|
||||
#[instrument(level = "trace", skip(ecx, body))]
|
||||
|
|
@ -94,7 +92,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
let intern_result = intern_const_alloc_recursive(ecx, intern_kind, &ret);
|
||||
|
||||
// Since evaluation had no errors, validate the resulting constant.
|
||||
const_validate_mplace(&ecx, &ret, cid)?;
|
||||
const_validate_mplace(ecx, &ret, cid)?;
|
||||
|
||||
// Only report this after validation, as validaiton produces much better diagnostics.
|
||||
// FIXME: ensure validation always reports this and stop making interning care about it.
|
||||
|
|
@ -105,18 +103,15 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
return Err(ecx
|
||||
.tcx
|
||||
.dcx()
|
||||
.emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })
|
||||
.emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })
|
||||
.into());
|
||||
}
|
||||
Err(InternResult::FoundBadMutablePointer) => {
|
||||
// only report mutable pointers if there were no dangling pointers
|
||||
let err_diag = errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind };
|
||||
ecx.tcx.emit_node_span_lint(
|
||||
lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE,
|
||||
ecx.machine.best_lint_scope(*ecx.tcx),
|
||||
err_diag.span,
|
||||
err_diag,
|
||||
)
|
||||
return Err(ecx
|
||||
.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind })
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -391,7 +386,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
|
|||
|
||||
#[inline(always)]
|
||||
fn const_validate_mplace<'tcx>(
|
||||
ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
|
||||
ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
|
||||
mplace: &MPlaceTy<'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
) -> Result<(), ErrorHandled> {
|
||||
|
|
@ -448,7 +443,12 @@ fn report_eval_error<'tcx>(
|
|||
error,
|
||||
DUMMY_SP,
|
||||
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
|
||||
|span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames },
|
||||
|span, frames| errors::ConstEvalError {
|
||||
span,
|
||||
error_kind: kind,
|
||||
instance,
|
||||
frame_notes: frames,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, LangItem, CRATE_HIR_ID};
|
||||
use rustc_middle::mir::AssertMessage;
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
|
@ -24,8 +24,8 @@ use crate::fluent_generated as fluent;
|
|||
use crate::interpret::{
|
||||
self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup,
|
||||
throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
|
||||
GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
|
||||
StackPopCleanup,
|
||||
GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic,
|
||||
RangeSet, Scalar, StackPopCleanup,
|
||||
};
|
||||
|
||||
/// When hitting this many interpreted terminators we emit a deny by default lint
|
||||
|
|
@ -65,6 +65,9 @@ pub struct CompileTimeMachine<'tcx> {
|
|||
/// storing the result in the given `AllocId`.
|
||||
/// Used to prevent reads from a static's base allocation, as that may allow for self-initialization loops.
|
||||
pub(crate) static_root_ids: Option<(AllocId, LocalDefId)>,
|
||||
|
||||
/// A cache of "data range" computations for unions (i.e., the offsets of non-padding bytes).
|
||||
union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
@ -99,6 +102,7 @@ impl<'tcx> CompileTimeMachine<'tcx> {
|
|||
can_access_mut_global,
|
||||
check_alignment,
|
||||
static_root_ids: None,
|
||||
union_data_ranges: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -714,16 +718,29 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
_kind: mir::RetagKind,
|
||||
val: &ImmTy<'tcx, CtfeProvenance>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, CtfeProvenance>> {
|
||||
// If it's a frozen shared reference that's not already immutable, make it immutable.
|
||||
// If it's a frozen shared reference that's not already immutable, potentially make it immutable.
|
||||
// (Do nothing on `None` provenance, that cannot store immutability anyway.)
|
||||
if let ty::Ref(_, ty, mutbl) = val.layout.ty.kind()
|
||||
&& *mutbl == Mutability::Not
|
||||
&& val.to_scalar_and_meta().0.to_pointer(ecx)?.provenance.is_some_and(|p| !p.immutable())
|
||||
// That next check is expensive, that's why we have all the guards above.
|
||||
&& ty.is_freeze(*ecx.tcx, ecx.param_env)
|
||||
&& val
|
||||
.to_scalar_and_meta()
|
||||
.0
|
||||
.to_pointer(ecx)?
|
||||
.provenance
|
||||
.is_some_and(|p| !p.immutable())
|
||||
{
|
||||
// That next check is expensive, that's why we have all the guards above.
|
||||
let is_immutable = ty.is_freeze(*ecx.tcx, ecx.param_env);
|
||||
let place = ecx.ref_to_mplace(val)?;
|
||||
let new_place = place.map_provenance(CtfeProvenance::as_immutable);
|
||||
let new_place = if is_immutable {
|
||||
place.map_provenance(CtfeProvenance::as_immutable)
|
||||
} else {
|
||||
// Even if it is not immutable, remember that it is a shared reference.
|
||||
// This allows it to become part of the final value of the constant.
|
||||
// (See <https://github.com/rust-lang/rust/pull/128543> for why we allow this
|
||||
// even when there is interior mutability.)
|
||||
place.map_provenance(CtfeProvenance::as_shared_ref)
|
||||
};
|
||||
Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout))
|
||||
} else {
|
||||
Ok(val.clone())
|
||||
|
|
@ -766,6 +783,19 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cached_union_data_range<'e>(
|
||||
ecx: &'e mut InterpCx<'tcx, Self>,
|
||||
ty: Ty<'tcx>,
|
||||
compute_range: impl FnOnce() -> RangeSet,
|
||||
) -> Cow<'e, RangeSet> {
|
||||
if ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks {
|
||||
Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
|
||||
} else {
|
||||
// Don't bother caching, we're only doing one validation at the end anyway.
|
||||
Cow::Owned(compute_range())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups
|
||||
|
|
|
|||
|
|
@ -35,13 +35,10 @@ pub(crate) struct NestedStaticInThreadLocal {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_mutable_ptr_in_final)]
|
||||
pub(crate) struct MutablePtrInFinal {
|
||||
// rust-lang/rust#122153: This was marked as `#[primary_span]` under
|
||||
// `derive(Diagnostic)`. Since we expect we may hard-error in future, we are
|
||||
// keeping the field (and skipping it under `derive(LintDiagnostic)`).
|
||||
#[skip_arg]
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: InternKind,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
ty::Ref(_, ty, _) => *ty,
|
||||
ty::RawPtr(ty, _) => *ty,
|
||||
// We only accept `Box` with the default allocator.
|
||||
_ if ty.is_box_global(*self.tcx) => ty.boxed_ty(),
|
||||
_ if ty.is_box_global(*self.tcx) => ty.expect_boxed_ty(),
|
||||
_ => return Ok(None),
|
||||
}))
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants};
|
|||
use tracing::{instrument, trace};
|
||||
|
||||
use super::{
|
||||
err_ub, throw_ub, ImmTy, InterpCx, InterpResult, Machine, Readable, Scalar, Writeable,
|
||||
err_ub, throw_ub, ImmTy, InterpCx, InterpResult, Machine, Projectable, Scalar, Writeable,
|
||||
};
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
|
|
@ -60,7 +60,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
#[instrument(skip(self), level = "trace")]
|
||||
pub fn read_discriminant(
|
||||
&self,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
op: &impl Projectable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, VariantIdx> {
|
||||
let ty = op.layout().ty;
|
||||
trace!("read_discriminant_value {:#?}", op.layout());
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use rustc_hir as hir;
|
|||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::sym;
|
||||
|
|
@ -223,37 +224,52 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
|
|||
continue;
|
||||
}
|
||||
|
||||
// Crucially, we check this *before* checking whether the `alloc_id`
|
||||
// has already been interned. The point of this check is to ensure that when
|
||||
// there are multiple pointers to the same allocation, they are *all* immutable.
|
||||
// Therefore it would be bad if we only checked the first pointer to any given
|
||||
// allocation.
|
||||
// Ensure that this is derived from a shared reference. Crucially, we check this *before*
|
||||
// checking whether the `alloc_id` has already been interned. The point of this check is to
|
||||
// ensure that when there are multiple pointers to the same allocation, they are *all*
|
||||
// derived from a shared reference. Therefore it would be bad if we only checked the first
|
||||
// pointer to any given allocation.
|
||||
// (It is likely not possible to actually have multiple pointers to the same allocation,
|
||||
// so alternatively we could also check that and ICE if there are multiple such pointers.)
|
||||
// See <https://github.com/rust-lang/rust/pull/128543> for why we are checking for "shared
|
||||
// reference" and not "immutable", i.e., for why we are allowing interior-mutable shared
|
||||
// references: they can actually be created in safe code while pointing to apparently
|
||||
// "immutable" values, via promotion or tail expression lifetime extension of
|
||||
// `&None::<Cell<T>>`.
|
||||
// We also exclude promoteds from this as `&mut []` can be promoted, which is a mutable
|
||||
// reference pointing to an immutable (zero-sized) allocation. We rely on the promotion
|
||||
// analysis not screwing up to ensure that it is sound to intern promoteds as immutable.
|
||||
if intern_kind != InternKind::Promoted
|
||||
&& inner_mutability == Mutability::Not
|
||||
&& !prov.immutable()
|
||||
&& !prov.shared_ref()
|
||||
{
|
||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some()
|
||||
&& !just_interned.contains(&alloc_id)
|
||||
{
|
||||
let is_already_global = ecx.tcx.try_get_global_alloc(alloc_id).is_some();
|
||||
if is_already_global && !just_interned.contains(&alloc_id) {
|
||||
// This is a pointer to some memory from another constant. We encounter mutable
|
||||
// pointers to such memory since we do not always track immutability through
|
||||
// these "global" pointers. Allowing them is harmless; the point of these checks
|
||||
// during interning is to justify why we intern the *new* allocations immutably,
|
||||
// so we can completely ignore existing allocations. We also don't need to add
|
||||
// this to the todo list, since after all it is already interned.
|
||||
// so we can completely ignore existing allocations.
|
||||
// We can also skip the rest of this loop iteration, since after all it is already
|
||||
// interned.
|
||||
continue;
|
||||
}
|
||||
// Found a mutable pointer inside a const where inner allocations should be
|
||||
// immutable. We exclude promoteds from this, since things like `&mut []` and
|
||||
// `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
|
||||
// on the promotion analysis not screwing up to ensure that it is sound to intern
|
||||
// promoteds as immutable.
|
||||
trace!("found bad mutable pointer");
|
||||
// Prefer dangling pointer errors over mutable pointer errors
|
||||
if result.is_ok() {
|
||||
result = Err(InternResult::FoundBadMutablePointer);
|
||||
// If this is a dangling pointer, that's actually fine -- the problematic case is
|
||||
// when there is memory there that someone might expect to be mutable, but we make it immutable.
|
||||
let dangling = !is_already_global && !ecx.memory.alloc_map.contains_key(&alloc_id);
|
||||
if !dangling {
|
||||
// Found a mutable reference inside a const where inner allocations should be
|
||||
// immutable.
|
||||
if !ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
|
||||
span_bug!(
|
||||
ecx.tcx.span,
|
||||
"the static const safety checks accepted mutable references they should not have accepted"
|
||||
);
|
||||
}
|
||||
// Prefer dangling pointer errors over mutable pointer errors
|
||||
if result.is_ok() {
|
||||
result = Err(InternResult::FoundBadMutablePointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
|
||||
|
|
@ -261,7 +277,6 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
|
|||
debug_assert!(!ecx.memory.alloc_map.contains_key(&alloc_id));
|
||||
continue;
|
||||
}
|
||||
just_interned.insert(alloc_id);
|
||||
// We always intern with `inner_mutability`, and furthermore we ensured above that if
|
||||
// that is "immutable", then there are *no* mutable pointers anywhere in the newly
|
||||
// interned memory -- justifying that we can indeed intern immutably. However this also
|
||||
|
|
@ -272,6 +287,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval
|
|||
// pointers before deciding which allocations can be made immutable; but for now we are
|
||||
// okay with losing some potential for immutability here. This can anyway only affect
|
||||
// `static mut`.
|
||||
just_interned.insert(alloc_id);
|
||||
match intern_shallow(ecx, alloc_id, inner_mutability) {
|
||||
Ok(nested) => todo.extend(nested),
|
||||
Err(()) => {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use rustc_apfloat::{Float, FloatConvert};
|
|||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -19,7 +20,7 @@ use rustc_target::spec::abi::Abi as CallAbi;
|
|||
use super::{
|
||||
throw_unsup, throw_unsup_format, AllocBytes, AllocId, AllocKind, AllocRange, Allocation,
|
||||
ConstAllocation, CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy,
|
||||
MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, CTFE_ALLOC_SALT,
|
||||
MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, CTFE_ALLOC_SALT,
|
||||
};
|
||||
|
||||
/// Data returned by [`Machine::after_stack_pop`], and consumed by
|
||||
|
|
@ -578,6 +579,15 @@ pub trait Machine<'tcx>: Sized {
|
|||
ecx: &InterpCx<'tcx, Self>,
|
||||
instance: Option<ty::Instance<'tcx>>,
|
||||
) -> usize;
|
||||
|
||||
fn cached_union_data_range<'e>(
|
||||
_ecx: &'e mut InterpCx<'tcx, Self>,
|
||||
_ty: Ty<'tcx>,
|
||||
compute_range: impl FnOnce() -> RangeSet,
|
||||
) -> Cow<'e, RangeSet> {
|
||||
// Default to no caching.
|
||||
Cow::Owned(compute_range())
|
||||
}
|
||||
}
|
||||
|
||||
/// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::{fmt, ptr};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
|
|
@ -118,7 +117,7 @@ pub struct Memory<'tcx, M: Machine<'tcx>> {
|
|||
/// This stores whether we are currently doing reads purely for the purpose of validation.
|
||||
/// Those reads do not trigger the machine's hooks for memory reads.
|
||||
/// Needless to say, this must only be set with great care!
|
||||
validation_in_progress: Cell<bool>,
|
||||
validation_in_progress: bool,
|
||||
}
|
||||
|
||||
/// A reference to some allocation that was already bounds-checked for the given region
|
||||
|
|
@ -145,7 +144,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> {
|
|||
alloc_map: M::MemoryMap::default(),
|
||||
extra_fn_ptr_map: FxIndexMap::default(),
|
||||
dead_alloc_map: FxIndexMap::default(),
|
||||
validation_in_progress: Cell::new(false),
|
||||
validation_in_progress: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -682,7 +681,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
|
||||
// accesses. That means we cannot rely on the closure above or the `Some` branch below. We
|
||||
// do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
|
||||
if !self.memory.validation_in_progress.get() {
|
||||
if !self.memory.validation_in_progress {
|
||||
if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) {
|
||||
M::before_alloc_read(self, alloc_id)?;
|
||||
}
|
||||
|
|
@ -690,7 +689,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
|
||||
let range = alloc_range(offset, size);
|
||||
if !self.memory.validation_in_progress.get() {
|
||||
if !self.memory.validation_in_progress {
|
||||
M::before_memory_read(
|
||||
self.tcx,
|
||||
&self.machine,
|
||||
|
|
@ -766,11 +765,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let parts = self.get_ptr_access(ptr, size)?;
|
||||
if let Some((alloc_id, offset, prov)) = parts {
|
||||
let tcx = self.tcx;
|
||||
let validation_in_progress = self.memory.validation_in_progress;
|
||||
// FIXME: can we somehow avoid looking up the allocation twice here?
|
||||
// We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`.
|
||||
let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?;
|
||||
let range = alloc_range(offset, size);
|
||||
M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?;
|
||||
if !validation_in_progress {
|
||||
M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?;
|
||||
}
|
||||
Ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id }))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
|
@ -1014,16 +1016,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
///
|
||||
/// We do this so Miri's allocation access tracking does not show the validation
|
||||
/// reads as spurious accesses.
|
||||
pub fn run_for_validation<R>(&self, f: impl FnOnce() -> R) -> R {
|
||||
pub fn run_for_validation<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
// This deliberately uses `==` on `bool` to follow the pattern
|
||||
// `assert!(val.replace(new) == old)`.
|
||||
assert!(
|
||||
self.memory.validation_in_progress.replace(true) == false,
|
||||
mem::replace(&mut self.memory.validation_in_progress, true) == false,
|
||||
"`validation_in_progress` was already set"
|
||||
);
|
||||
let res = f();
|
||||
let res = f(self);
|
||||
assert!(
|
||||
self.memory.validation_in_progress.replace(false) == true,
|
||||
mem::replace(&mut self.memory.validation_in_progress, false) == true,
|
||||
"`validation_in_progress` was unset by someone else"
|
||||
);
|
||||
res
|
||||
|
|
@ -1115,6 +1117,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> {
|
|||
impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes>
|
||||
AllocRefMut<'a, 'tcx, Prov, Extra, Bytes>
|
||||
{
|
||||
pub fn as_ref<'b>(&'b self) -> AllocRef<'b, 'tcx, Prov, Extra, Bytes> {
|
||||
AllocRef { alloc: self.alloc, range: self.range, tcx: self.tcx, alloc_id: self.alloc_id }
|
||||
}
|
||||
|
||||
/// `range` is relative to this allocation reference, not the base of the allocation.
|
||||
pub fn write_scalar(&mut self, range: AllocRange, val: Scalar<Prov>) -> InterpResult<'tcx> {
|
||||
let range = self.range.subrange(range);
|
||||
|
|
@ -1130,13 +1136,30 @@ impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes>
|
|||
self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val)
|
||||
}
|
||||
|
||||
/// Mark the given sub-range (relative to this allocation reference) as uninitialized.
|
||||
pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> {
|
||||
let range = self.range.subrange(range);
|
||||
Ok(self
|
||||
.alloc
|
||||
.write_uninit(&self.tcx, range)
|
||||
.map_err(|e| e.to_interp_error(self.alloc_id))?)
|
||||
}
|
||||
|
||||
/// Mark the entire referenced range as uninitialized
|
||||
pub fn write_uninit(&mut self) -> InterpResult<'tcx> {
|
||||
pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> {
|
||||
Ok(self
|
||||
.alloc
|
||||
.write_uninit(&self.tcx, self.range)
|
||||
.map_err(|e| e.to_interp_error(self.alloc_id))?)
|
||||
}
|
||||
|
||||
/// Remove all provenance in the reference range.
|
||||
pub fn clear_provenance(&mut self) -> InterpResult<'tcx> {
|
||||
Ok(self
|
||||
.alloc
|
||||
.clear_provenance(&self.tcx, self.range)
|
||||
.map_err(|e| e.to_interp_error(self.alloc_id))?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Prov, Extra, Bytes> {
|
||||
|
|
@ -1278,7 +1301,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
};
|
||||
let src_alloc = self.get_alloc_raw(src_alloc_id)?;
|
||||
let src_range = alloc_range(src_offset, size);
|
||||
assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
|
||||
assert!(!self.memory.validation_in_progress, "we can't be copying during validation");
|
||||
M::before_memory_read(
|
||||
tcx,
|
||||
&self.machine,
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ pub(crate) use self::intrinsics::eval_nullary_intrinsic;
|
|||
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, ReturnAction};
|
||||
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||
use self::operand::Operand;
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy};
|
||||
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
||||
use self::place::{MemPlace, Place};
|
||||
pub use self::projection::{OffsetMode, Projectable};
|
||||
pub use self::stack::{Frame, FrameInfo, LocalState, StackPopCleanup, StackPopInfo};
|
||||
pub(crate) use self::util::create_static_alloc;
|
||||
pub use self::validity::{CtfeValidationMode, RefTracking};
|
||||
pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking};
|
||||
pub use self::visitor::ValueVisitor;
|
||||
|
|
|
|||
|
|
@ -111,6 +111,46 @@ impl<Prov: Provenance> Immediate<Prov> {
|
|||
Immediate::Uninit => bug!("Got uninit where a scalar or scalar pair was expected"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert that this immediate is a valid value for the given ABI.
|
||||
pub fn assert_matches_abi(self, abi: Abi, cx: &impl HasDataLayout) {
|
||||
match (self, abi) {
|
||||
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
|
||||
assert_eq!(scalar.size(), s.size(cx));
|
||||
if !matches!(s.primitive(), abi::Pointer(..)) {
|
||||
assert!(matches!(scalar, Scalar::Int(..)));
|
||||
}
|
||||
}
|
||||
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
|
||||
assert_eq!(a_val.size(), a.size(cx));
|
||||
if !matches!(a.primitive(), abi::Pointer(..)) {
|
||||
assert!(matches!(a_val, Scalar::Int(..)));
|
||||
}
|
||||
assert_eq!(b_val.size(), b.size(cx));
|
||||
if !matches!(b.primitive(), abi::Pointer(..)) {
|
||||
assert!(matches!(b_val, Scalar::Int(..)));
|
||||
}
|
||||
}
|
||||
(Immediate::Uninit, _) => {}
|
||||
_ => {
|
||||
bug!("value {self:?} does not match ABI {abi:?})",)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_provenance<'tcx>(&mut self) -> InterpResult<'tcx> {
|
||||
match self {
|
||||
Immediate::Scalar(s) => {
|
||||
s.clear_provenance()?;
|
||||
}
|
||||
Immediate::ScalarPair(a, b) => {
|
||||
a.clear_provenance()?;
|
||||
b.clear_provenance()?;
|
||||
}
|
||||
Immediate::Uninit => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ScalarPair needs a type to interpret, so we often have an immediate and a type together
|
||||
|
|
@ -490,32 +530,6 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The `Readable` trait describes interpreter values that one can read from.
|
||||
pub trait Readable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>>;
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
self.as_mplace_or_imm()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
Left(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for ImmTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
Right(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
|
||||
/// Returns `None` if the layout does not permit loading this as a value.
|
||||
|
|
@ -588,9 +602,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// ConstProp needs it, though.
|
||||
pub fn read_immediate_raw(
|
||||
&self,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Either<MPlaceTy<'tcx, M::Provenance>, ImmTy<'tcx, M::Provenance>>> {
|
||||
Ok(match src.as_mplace_or_imm() {
|
||||
Ok(match src.to_op(self)?.as_mplace_or_imm() {
|
||||
Left(ref mplace) => {
|
||||
if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? {
|
||||
Right(val)
|
||||
|
|
@ -608,7 +622,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
#[inline(always)]
|
||||
pub fn read_immediate(
|
||||
&self,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
op: &impl Projectable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
if !matches!(
|
||||
op.layout().abi,
|
||||
|
|
@ -627,7 +641,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// Read a scalar from a place
|
||||
pub fn read_scalar(
|
||||
&self,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
op: &impl Projectable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
Ok(self.read_immediate(op)?.to_scalar())
|
||||
}
|
||||
|
|
@ -638,21 +652,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// Read a pointer from a place.
|
||||
pub fn read_pointer(
|
||||
&self,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
op: &impl Projectable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
|
||||
self.read_scalar(op)?.to_pointer(self)
|
||||
}
|
||||
/// Read a pointer-sized unsigned integer from a place.
|
||||
pub fn read_target_usize(
|
||||
&self,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
op: &impl Projectable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, u64> {
|
||||
self.read_scalar(op)?.to_target_usize(self)
|
||||
}
|
||||
/// Read a pointer-sized signed integer from a place.
|
||||
pub fn read_target_isize(
|
||||
&self,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
op: &impl Projectable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, i64> {
|
||||
self.read_scalar(op)?.to_target_isize(self)
|
||||
}
|
||||
|
|
@ -717,7 +731,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
match place.as_mplace_or_local() {
|
||||
Left(mplace) => Ok(mplace.into()),
|
||||
Right((local, offset, locals_addr)) => {
|
||||
Right((local, offset, locals_addr, _)) => {
|
||||
debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`.
|
||||
debug_assert_eq!(locals_addr, self.frame().locals_addr());
|
||||
let base = self.local_to_op(local, None)?;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use tracing::{instrument, trace};
|
|||
use super::{
|
||||
alloc_range, mir_assign_valid_types, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance,
|
||||
ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy,
|
||||
Operand, Pointer, Projectable, Provenance, Readable, Scalar,
|
||||
Operand, Pointer, Projectable, Provenance, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
|
|
@ -180,7 +180,8 @@ pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
|
|||
Ptr(MemPlace<Prov>),
|
||||
|
||||
/// To support alloc-free locals, we are able to write directly to a local. The offset indicates
|
||||
/// where in the local this place is located; if it is `None`, no projection has been applied.
|
||||
/// where in the local this place is located; if it is `None`, no projection has been applied
|
||||
/// and the type of the place is exactly the type of the local.
|
||||
/// Such projections are meaningful even if the offset is 0, since they can change layouts.
|
||||
/// (Without that optimization, we'd just always be a `MemPlace`.)
|
||||
/// `Local` places always refer to the current stack frame, so they are unstable under
|
||||
|
|
@ -231,10 +232,12 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
|
|||
#[inline(always)]
|
||||
pub fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize)> {
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize, TyAndLayout<'tcx>)> {
|
||||
match self.place {
|
||||
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout }),
|
||||
Place::Local { local, offset, locals_addr } => Right((local, offset, locals_addr)),
|
||||
Place::Local { local, offset, locals_addr } => {
|
||||
Right((local, offset, locals_addr, self.layout))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -277,7 +280,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
|||
) -> InterpResult<'tcx, Self> {
|
||||
Ok(match self.as_mplace_or_local() {
|
||||
Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(),
|
||||
Right((local, old_offset, locals_addr)) => {
|
||||
Right((local, old_offset, locals_addr, _)) => {
|
||||
debug_assert!(layout.is_sized(), "unsized locals should live in memory");
|
||||
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
|
||||
// `Place::Local` are always in-bounds of their surrounding local, so we can just
|
||||
|
|
@ -328,9 +331,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
|||
|
||||
/// The `Weiteable` trait describes interpreter values that can be written to.
|
||||
pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize, TyAndLayout<'tcx>)>;
|
||||
fn to_place(&self) -> PlaceTy<'tcx, Prov>;
|
||||
|
||||
fn force_mplace<M: Machine<'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
|
|
@ -340,11 +341,8 @@ pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
|||
|
||||
impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize, TyAndLayout<'tcx>)> {
|
||||
self.as_mplace_or_local()
|
||||
.map_right(|(local, offset, locals_addr)| (local, offset, locals_addr, self.layout))
|
||||
fn to_place(&self) -> PlaceTy<'tcx, Prov> {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
@ -358,10 +356,8 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
|||
|
||||
impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize, TyAndLayout<'tcx>)> {
|
||||
Left(self.clone())
|
||||
fn to_place(&self) -> PlaceTy<'tcx, Prov> {
|
||||
self.clone().into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
@ -436,7 +432,7 @@ where
|
|||
#[instrument(skip(self), level = "trace")]
|
||||
pub fn deref_pointer(
|
||||
&self,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
if src.layout().ty.is_box() {
|
||||
// Derefer should have removed all Box derefs.
|
||||
|
|
@ -562,6 +558,40 @@ where
|
|||
Ok(place)
|
||||
}
|
||||
|
||||
/// Given a place, returns either the underlying mplace or a reference to where the value of
|
||||
/// this place is stored.
|
||||
fn as_mplace_or_mutable_local(
|
||||
&mut self,
|
||||
place: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<
|
||||
'tcx,
|
||||
Either<MPlaceTy<'tcx, M::Provenance>, (&mut Immediate<M::Provenance>, TyAndLayout<'tcx>)>,
|
||||
> {
|
||||
Ok(match place.to_place().as_mplace_or_local() {
|
||||
Left(mplace) => Left(mplace),
|
||||
Right((local, offset, locals_addr, layout)) => {
|
||||
if offset.is_some() {
|
||||
// This has been projected to a part of this local, or had the type changed.
|
||||
// FIMXE: there are cases where we could still avoid allocating an mplace.
|
||||
Left(place.force_mplace(self)?)
|
||||
} else {
|
||||
debug_assert_eq!(locals_addr, self.frame().locals_addr());
|
||||
debug_assert_eq!(self.layout_of_local(self.frame(), local, None)?, layout);
|
||||
match self.frame_mut().locals[local].access_mut()? {
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory.
|
||||
Left(MPlaceTy { mplace: *mplace, layout })
|
||||
}
|
||||
Operand::Immediate(local_val) => {
|
||||
// The local still has the optimized representation.
|
||||
Right((local_val, layout))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Write an immediate to a place
|
||||
#[inline(always)]
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
|
|
@ -574,9 +604,11 @@ where
|
|||
|
||||
if M::enforce_validity(self, dest.layout()) {
|
||||
// Data got changed, better make sure it matches the type!
|
||||
// Also needed to reset padding.
|
||||
self.validate_operand(
|
||||
&dest.to_op(self)?,
|
||||
&dest.to_place(),
|
||||
M::enforce_validity_recursively(self, dest.layout()),
|
||||
/*reset_provenance_and_padding*/ true,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
@ -606,67 +638,27 @@ where
|
|||
/// Write an immediate to a place.
|
||||
/// If you use this you are responsible for validating that things got copied at the
|
||||
/// right type.
|
||||
fn write_immediate_no_validate(
|
||||
pub(super) fn write_immediate_no_validate(
|
||||
&mut self,
|
||||
src: Immediate<M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
assert!(dest.layout().is_sized(), "Cannot write unsized immediate data");
|
||||
|
||||
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
|
||||
// but not factored as a separate function.
|
||||
let mplace = match dest.as_mplace_or_local() {
|
||||
Right((local, offset, locals_addr, layout)) => {
|
||||
if offset.is_some() {
|
||||
// This has been projected to a part of this local. We could have complicated
|
||||
// logic to still keep this local as an `Operand`... but it's much easier to
|
||||
// just fall back to the indirect path.
|
||||
dest.force_mplace(self)?
|
||||
} else {
|
||||
debug_assert_eq!(locals_addr, self.frame().locals_addr());
|
||||
match self.frame_mut().locals[local].access_mut()? {
|
||||
Operand::Immediate(local_val) => {
|
||||
// Local can be updated in-place.
|
||||
*local_val = src;
|
||||
// Double-check that the value we are storing and the local fit to each other.
|
||||
// (*After* doing the update for borrow checker reasons.)
|
||||
if cfg!(debug_assertions) {
|
||||
let local_layout =
|
||||
self.layout_of_local(&self.frame(), local, None)?;
|
||||
match (src, local_layout.abi) {
|
||||
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
|
||||
assert_eq!(scalar.size(), s.size(self))
|
||||
}
|
||||
(
|
||||
Immediate::ScalarPair(a_val, b_val),
|
||||
Abi::ScalarPair(a, b),
|
||||
) => {
|
||||
assert_eq!(a_val.size(), a.size(self));
|
||||
assert_eq!(b_val.size(), b.size(self));
|
||||
}
|
||||
(Immediate::Uninit, _) => {}
|
||||
(src, abi) => {
|
||||
bug!(
|
||||
"value {src:?} cannot be written into local with type {} (ABI {abi:?})",
|
||||
local_layout.ty
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory, go on below.
|
||||
MPlaceTy { mplace: *mplace, layout }
|
||||
}
|
||||
}
|
||||
match self.as_mplace_or_mutable_local(&dest.to_place())? {
|
||||
Right((local_val, local_layout)) => {
|
||||
// Local can be updated in-place.
|
||||
*local_val = src;
|
||||
// Double-check that the value we are storing and the local fit to each other.
|
||||
if cfg!(debug_assertions) {
|
||||
src.assert_matches_abi(local_layout.abi, self);
|
||||
}
|
||||
}
|
||||
Left(mplace) => mplace, // already referring to memory
|
||||
};
|
||||
|
||||
// This is already in memory, write there.
|
||||
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)
|
||||
Left(mplace) => {
|
||||
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write an immediate to memory.
|
||||
|
|
@ -678,6 +670,9 @@ where
|
|||
layout: TyAndLayout<'tcx>,
|
||||
dest: MemPlace<M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
if cfg!(debug_assertions) {
|
||||
value.assert_matches_abi(layout.abi, self);
|
||||
}
|
||||
// Note that it is really important that the type here is the right one, and matches the
|
||||
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
|
||||
// to handle padding properly, which is only correct if we never look at this data with the
|
||||
|
|
@ -691,15 +686,7 @@ where
|
|||
|
||||
match value {
|
||||
Immediate::Scalar(scalar) => {
|
||||
let Abi::Scalar(s) = layout.abi else {
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"write_immediate_to_mplace: invalid Scalar layout: {layout:#?}",
|
||||
)
|
||||
};
|
||||
let size = s.size(&tcx);
|
||||
assert_eq!(size, layout.size, "abi::Scalar size does not match layout size");
|
||||
alloc.write_scalar(alloc_range(Size::ZERO, size), scalar)
|
||||
alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)
|
||||
}
|
||||
Immediate::ScalarPair(a_val, b_val) => {
|
||||
let Abi::ScalarPair(a, b) = layout.abi else {
|
||||
|
|
@ -709,18 +696,19 @@ where
|
|||
layout
|
||||
)
|
||||
};
|
||||
let (a_size, b_size) = (a.size(&tcx), b.size(&tcx));
|
||||
let b_offset = a_size.align_to(b.align(&tcx).abi);
|
||||
let b_offset = a.size(&tcx).align_to(b.align(&tcx).abi);
|
||||
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
|
||||
|
||||
// It is tempting to verify `b_offset` against `layout.fields.offset(1)`,
|
||||
// but that does not work: We could be a newtype around a pair, then the
|
||||
// fields do not match the `ScalarPair` components.
|
||||
|
||||
alloc.write_scalar(alloc_range(Size::ZERO, a_size), a_val)?;
|
||||
alloc.write_scalar(alloc_range(b_offset, b_size), b_val)
|
||||
alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?;
|
||||
alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?;
|
||||
// We don't have to reset padding here, `write_immediate` will anyway do a validation run.
|
||||
Ok(())
|
||||
}
|
||||
Immediate::Uninit => alloc.write_uninit(),
|
||||
Immediate::Uninit => alloc.write_uninit_full(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -728,35 +716,38 @@ where
|
|||
&mut self,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let mplace = match dest.as_mplace_or_local() {
|
||||
Left(mplace) => mplace,
|
||||
Right((local, offset, locals_addr, layout)) => {
|
||||
if offset.is_some() {
|
||||
// This has been projected to a part of this local. We could have complicated
|
||||
// logic to still keep this local as an `Operand`... but it's much easier to
|
||||
// just fall back to the indirect path.
|
||||
// FIXME: share the logic with `write_immediate_no_validate`.
|
||||
dest.force_mplace(self)?
|
||||
} else {
|
||||
debug_assert_eq!(locals_addr, self.frame().locals_addr());
|
||||
match self.frame_mut().locals[local].access_mut()? {
|
||||
Operand::Immediate(local) => {
|
||||
*local = Immediate::Uninit;
|
||||
return Ok(());
|
||||
}
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory, go on below.
|
||||
MPlaceTy { mplace: *mplace, layout }
|
||||
}
|
||||
}
|
||||
}
|
||||
match self.as_mplace_or_mutable_local(&dest.to_place())? {
|
||||
Right((local_val, _local_layout)) => {
|
||||
*local_val = Immediate::Uninit;
|
||||
}
|
||||
};
|
||||
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
|
||||
// Zero-sized access
|
||||
return Ok(());
|
||||
};
|
||||
alloc.write_uninit()?;
|
||||
Left(mplace) => {
|
||||
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
|
||||
// Zero-sized access
|
||||
return Ok(());
|
||||
};
|
||||
alloc.write_uninit_full()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove all provenance in the given place.
|
||||
pub fn clear_provenance(
|
||||
&mut self,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
match self.as_mplace_or_mutable_local(&dest.to_place())? {
|
||||
Right((local_val, _local_layout)) => {
|
||||
local_val.clear_provenance()?;
|
||||
}
|
||||
Left(mplace) => {
|
||||
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
|
||||
// Zero-sized access
|
||||
return Ok(());
|
||||
};
|
||||
alloc.clear_provenance()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -768,7 +759,7 @@ where
|
|||
#[inline(always)]
|
||||
pub(super) fn copy_op_no_dest_validation(
|
||||
&mut self,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
|
|
@ -781,7 +772,7 @@ where
|
|||
#[inline(always)]
|
||||
pub fn copy_op_allow_transmute(
|
||||
&mut self,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
|
|
@ -794,7 +785,7 @@ where
|
|||
#[inline(always)]
|
||||
pub fn copy_op(
|
||||
&mut self,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
|
|
@ -808,28 +799,35 @@ where
|
|||
#[instrument(skip(self), level = "trace")]
|
||||
fn copy_op_inner(
|
||||
&mut self,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
allow_transmute: bool,
|
||||
validate_dest: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Generally for transmutation, data must be valid both at the old and new type.
|
||||
// But if the types are the same, the 2nd validation below suffices.
|
||||
if src.layout().ty != dest.layout().ty && M::enforce_validity(self, src.layout()) {
|
||||
self.validate_operand(
|
||||
&src.to_op(self)?,
|
||||
M::enforce_validity_recursively(self, src.layout()),
|
||||
)?;
|
||||
}
|
||||
// These are technically *two* typed copies: `src` is a not-yet-loaded value,
|
||||
// so we're going a typed copy at `src` type from there to some intermediate storage.
|
||||
// And then we're doing a second typed copy from that intermediate storage to `dest`.
|
||||
// But as an optimization, we only make a single direct copy here.
|
||||
|
||||
// Do the actual copy.
|
||||
self.copy_op_no_validate(src, dest, allow_transmute)?;
|
||||
|
||||
if validate_dest && M::enforce_validity(self, dest.layout()) {
|
||||
// Data got changed, better make sure it matches the type!
|
||||
let dest = dest.to_place();
|
||||
// Given that there were two typed copies, we have to ensure this is valid at both types,
|
||||
// and we have to ensure this loses provenance and padding according to both types.
|
||||
// But if the types are identical, we only do one pass.
|
||||
if allow_transmute && src.layout().ty != dest.layout().ty {
|
||||
self.validate_operand(
|
||||
&dest.transmute(src.layout(), self)?,
|
||||
M::enforce_validity_recursively(self, src.layout()),
|
||||
/*reset_provenance_and_padding*/ true,
|
||||
)?;
|
||||
}
|
||||
self.validate_operand(
|
||||
&dest.to_op(self)?,
|
||||
&dest,
|
||||
M::enforce_validity_recursively(self, dest.layout()),
|
||||
/*reset_provenance_and_padding*/ true,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
@ -843,7 +841,7 @@ where
|
|||
#[instrument(skip(self), level = "trace")]
|
||||
fn copy_op_no_validate(
|
||||
&mut self,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
allow_transmute: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
//! That's useful because it means other passes (e.g. promotion) can rely on `const`s
|
||||
//! to be const-safe.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZero;
|
||||
|
|
@ -13,25 +14,25 @@ use hir::def::DefKind;
|
|||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::interpret::ValidationErrorKind::{self, *};
|
||||
use rustc_middle::mir::interpret::{
|
||||
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
|
||||
alloc_range, ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
|
||||
UnsupportedOpInfo, ValidationErrorInfo,
|
||||
};
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{
|
||||
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
|
||||
Abi, FieldIdx, FieldsShape, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
|
||||
};
|
||||
use tracing::trace;
|
||||
|
||||
use super::machine::AllocMap;
|
||||
use super::{
|
||||
err_ub, format_interp_error, throw_ub, AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy,
|
||||
Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable,
|
||||
Scalar, ValueVisitor,
|
||||
Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, PlaceTy, Pointer,
|
||||
Projectable, Scalar, ValueVisitor,
|
||||
};
|
||||
|
||||
// for the validation errors
|
||||
|
|
@ -125,6 +126,7 @@ pub enum PathElem {
|
|||
EnumTag,
|
||||
CoroutineTag,
|
||||
DynDowncast,
|
||||
Vtable,
|
||||
}
|
||||
|
||||
/// Extra things to check for during validation of CTFE results.
|
||||
|
|
@ -163,22 +165,22 @@ impl<T: Clone + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH>
|
|||
pub fn empty() -> Self {
|
||||
RefTracking { seen: FxHashSet::default(), todo: vec![] }
|
||||
}
|
||||
pub fn new(op: T) -> Self {
|
||||
pub fn new(val: T) -> Self {
|
||||
let mut ref_tracking_for_consts =
|
||||
RefTracking { seen: FxHashSet::default(), todo: vec![(op.clone(), PATH::default())] };
|
||||
ref_tracking_for_consts.seen.insert(op);
|
||||
RefTracking { seen: FxHashSet::default(), todo: vec![(val.clone(), PATH::default())] };
|
||||
ref_tracking_for_consts.seen.insert(val);
|
||||
ref_tracking_for_consts
|
||||
}
|
||||
pub fn next(&mut self) -> Option<(T, PATH)> {
|
||||
self.todo.pop()
|
||||
}
|
||||
|
||||
fn track(&mut self, op: T, path: impl FnOnce() -> PATH) {
|
||||
if self.seen.insert(op.clone()) {
|
||||
trace!("Recursing below ptr {:#?}", op);
|
||||
fn track(&mut self, val: T, path: impl FnOnce() -> PATH) {
|
||||
if self.seen.insert(val.clone()) {
|
||||
trace!("Recursing below ptr {:#?}", val);
|
||||
let path = path();
|
||||
// Remember to come back to this later.
|
||||
self.todo.push((op, path));
|
||||
self.todo.push((val, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -204,11 +206,62 @@ fn write_path(out: &mut String, path: &[PathElem]) {
|
|||
// not the root.
|
||||
Deref => write!(out, ".<deref>"),
|
||||
DynDowncast => write!(out, ".<dyn-downcast>"),
|
||||
Vtable => write!(out, ".<vtable>"),
|
||||
}
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a set of `Size` values as a sorted list of ranges.
|
||||
// These are (offset, length) pairs, and they are sorted and mutually disjoint,
|
||||
// and never adjacent (i.e. there's always a gap between two of them).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RangeSet(Vec<(Size, Size)>);
|
||||
|
||||
impl RangeSet {
|
||||
fn add_range(&mut self, offset: Size, size: Size) {
|
||||
if size.bytes() == 0 {
|
||||
// No need to track empty ranges.
|
||||
return;
|
||||
}
|
||||
let v = &mut self.0;
|
||||
// We scan for a partition point where the left partition is all the elements that end
|
||||
// strictly before we start. Those are elements that are too "low" to merge with us.
|
||||
let idx =
|
||||
v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset);
|
||||
// Now we want to either merge with the first element of the second partition, or insert ourselves before that.
|
||||
if let Some(&(other_offset, other_size)) = v.get(idx)
|
||||
&& offset + size >= other_offset
|
||||
{
|
||||
// Their end is >= our start (otherwise it would not be in the 2nd partition) and
|
||||
// our end is >= their start. This means we can merge the ranges.
|
||||
let new_start = other_offset.min(offset);
|
||||
let mut new_end = (other_offset + other_size).max(offset + size);
|
||||
// We grew to the right, so merge with overlapping/adjacent elements.
|
||||
// (We also may have grown to the left, but that can never make us adjacent with
|
||||
// anything there since we selected the first such candidate via `partition_point`.)
|
||||
let mut scan_right = 1;
|
||||
while let Some(&(next_offset, next_size)) = v.get(idx + scan_right)
|
||||
&& new_end >= next_offset
|
||||
{
|
||||
// Increase our size to absorb the next element.
|
||||
new_end = new_end.max(next_offset + next_size);
|
||||
// Look at the next element.
|
||||
scan_right += 1;
|
||||
}
|
||||
// Update the element we grew.
|
||||
v[idx] = (new_start, new_end - new_start);
|
||||
// Remove the elements we absorbed (if any).
|
||||
if scan_right > 1 {
|
||||
drop(v.drain((idx + 1)..(idx + scan_right)));
|
||||
}
|
||||
} else {
|
||||
// Insert new element.
|
||||
v.insert(idx, (offset, size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> {
|
||||
/// The `path` may be pushed to, but the part that is present when a function
|
||||
/// starts must not be changed! `visit_fields` and `visit_array` rely on
|
||||
|
|
@ -217,7 +270,17 @@ struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> {
|
|||
ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>,
|
||||
/// `None` indicates this is not validating for CTFE (but for runtime).
|
||||
ctfe_mode: Option<CtfeValidationMode>,
|
||||
ecx: &'rt InterpCx<'tcx, M>,
|
||||
ecx: &'rt mut InterpCx<'tcx, M>,
|
||||
/// Whether provenance should be reset outside of pointers (emulating the effect of a typed
|
||||
/// copy).
|
||||
reset_provenance_and_padding: bool,
|
||||
/// This tracks which byte ranges in this value contain data; the remaining bytes are padding.
|
||||
/// The ideal representation here would be pointer-length pairs, but to keep things more compact
|
||||
/// we only store a (range) set of offsets -- the base pointer is the same throughout the entire
|
||||
/// visit, after all.
|
||||
/// If this is `Some`, then `reset_provenance_and_padding` must be true (but not vice versa:
|
||||
/// we might not track data vs padding bytes if the operand isn't stored in memory anyway).
|
||||
data_bytes: Option<RangeSet>,
|
||||
}
|
||||
|
||||
impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||
|
|
@ -287,8 +350,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
// arrays/slices
|
||||
ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field),
|
||||
|
||||
// dyn* vtables
|
||||
ty::Dynamic(_, _, ty::DynKind::DynStar) if field == 1 => PathElem::Vtable,
|
||||
|
||||
// dyn traits
|
||||
ty::Dynamic(..) => PathElem::DynDowncast,
|
||||
ty::Dynamic(..) => {
|
||||
assert_eq!(field, 0);
|
||||
PathElem::DynDowncast
|
||||
}
|
||||
|
||||
// nothing else has an aggregate layout
|
||||
_ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", layout.ty),
|
||||
|
|
@ -314,11 +383,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
|
||||
fn read_immediate(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
expected: ExpectedKind,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
Ok(try_validation!(
|
||||
self.ecx.read_immediate(op),
|
||||
self.ecx.read_immediate(val),
|
||||
self.path,
|
||||
Ub(InvalidUninitBytes(None)) =>
|
||||
Uninit { expected },
|
||||
|
|
@ -332,10 +401,40 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
|
||||
fn read_scalar(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
expected: ExpectedKind,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
Ok(self.read_immediate(op, expected)?.to_scalar())
|
||||
Ok(self.read_immediate(val, expected)?.to_scalar())
|
||||
}
|
||||
|
||||
fn deref_pointer(
|
||||
&mut self,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
expected: ExpectedKind,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
// Not using `ecx.deref_pointer` since we want to use our `read_immediate` wrapper.
|
||||
let imm = self.read_immediate(val, expected)?;
|
||||
// Reset provenance: ensure slice tail metadata does not preserve provenance,
|
||||
// and ensure all pointers do not preserve partial provenance.
|
||||
if self.reset_provenance_and_padding {
|
||||
if matches!(imm.layout.abi, Abi::Scalar(..)) {
|
||||
// A thin pointer. If it has provenance, we don't have to do anything.
|
||||
// If it does not, ensure we clear the provenance in memory.
|
||||
if matches!(imm.to_scalar(), Scalar::Int(..)) {
|
||||
self.ecx.clear_provenance(val)?;
|
||||
}
|
||||
} else {
|
||||
// A wide pointer. This means we have to worry both about the pointer itself and the
|
||||
// metadata. We do the lazy thing and just write back the value we got. Just
|
||||
// clearing provenance in a targeted manner would be more efficient, but unless this
|
||||
// is a perf hotspot it's just not worth the effort.
|
||||
self.ecx.write_immediate_no_validate(*imm, val)?;
|
||||
}
|
||||
// The entire thing is data, not padding.
|
||||
self.add_data_range_place(val);
|
||||
}
|
||||
// Now turn it into a place.
|
||||
self.ecx.ref_to_mplace(&imm)
|
||||
}
|
||||
|
||||
fn check_wide_ptr_meta(
|
||||
|
|
@ -376,11 +475,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
/// Check a reference or `Box`.
|
||||
fn check_safe_pointer(
|
||||
&mut self,
|
||||
value: &OpTy<'tcx, M::Provenance>,
|
||||
value: &PlaceTy<'tcx, M::Provenance>,
|
||||
ptr_kind: PointerKind,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Not using `deref_pointer` since we want to use our `read_immediate` wrapper.
|
||||
let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ptr_kind.into())?)?;
|
||||
let place = self.deref_pointer(value, ptr_kind.into())?;
|
||||
// Handle wide pointers.
|
||||
// Check metadata early, for better diagnostics
|
||||
if place.layout.is_unsized() {
|
||||
|
|
@ -519,6 +617,13 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
if ptr_expected_mutbl == Mutability::Mut
|
||||
&& alloc_actual_mutbl == Mutability::Not
|
||||
{
|
||||
if !self.ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you
|
||||
{
|
||||
span_bug!(
|
||||
self.ecx.tcx.span,
|
||||
"the static const safety checks accepted mutable references they should not have accepted"
|
||||
);
|
||||
}
|
||||
throw_validation_failure!(self.path, MutableRefToImmutable);
|
||||
}
|
||||
// In a const, everything must be completely immutable.
|
||||
|
|
@ -564,31 +669,39 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
/// Note that not all of these have `FieldsShape::Primitive`, e.g. wide references.
|
||||
fn try_visit_primitive(
|
||||
&mut self,
|
||||
value: &OpTy<'tcx, M::Provenance>,
|
||||
value: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, bool> {
|
||||
// Go over all the primitive types
|
||||
let ty = value.layout.ty;
|
||||
match ty.kind() {
|
||||
ty::Bool => {
|
||||
let value = self.read_scalar(value, ExpectedKind::Bool)?;
|
||||
let scalar = self.read_scalar(value, ExpectedKind::Bool)?;
|
||||
try_validation!(
|
||||
value.to_bool(),
|
||||
scalar.to_bool(),
|
||||
self.path,
|
||||
Ub(InvalidBool(..)) => ValidationErrorKind::InvalidBool {
|
||||
value: format!("{value:x}"),
|
||||
value: format!("{scalar:x}"),
|
||||
}
|
||||
);
|
||||
if self.reset_provenance_and_padding {
|
||||
self.ecx.clear_provenance(value)?;
|
||||
self.add_data_range_place(value);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
ty::Char => {
|
||||
let value = self.read_scalar(value, ExpectedKind::Char)?;
|
||||
let scalar = self.read_scalar(value, ExpectedKind::Char)?;
|
||||
try_validation!(
|
||||
value.to_char(),
|
||||
scalar.to_char(),
|
||||
self.path,
|
||||
Ub(InvalidChar(..)) => ValidationErrorKind::InvalidChar {
|
||||
value: format!("{value:x}"),
|
||||
value: format!("{scalar:x}"),
|
||||
}
|
||||
);
|
||||
if self.reset_provenance_and_padding {
|
||||
self.ecx.clear_provenance(value)?;
|
||||
self.add_data_range_place(value);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
|
||||
|
|
@ -602,11 +715,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
ExpectedKind::Int
|
||||
},
|
||||
)?;
|
||||
if self.reset_provenance_and_padding {
|
||||
self.ecx.clear_provenance(value)?;
|
||||
self.add_data_range_place(value);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
ty::RawPtr(..) => {
|
||||
let place =
|
||||
self.ecx.ref_to_mplace(&self.read_immediate(value, ExpectedKind::RawPtr)?)?;
|
||||
let place = self.deref_pointer(value, ExpectedKind::RawPtr)?;
|
||||
if place.layout.is_unsized() {
|
||||
self.check_wide_ptr_meta(place.meta(), place.layout)?;
|
||||
}
|
||||
|
|
@ -617,11 +733,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
Ok(true)
|
||||
}
|
||||
ty::FnPtr(..) => {
|
||||
let value = self.read_scalar(value, ExpectedKind::FnPtr)?;
|
||||
let scalar = self.read_scalar(value, ExpectedKind::FnPtr)?;
|
||||
|
||||
// If we check references recursively, also check that this points to a function.
|
||||
if let Some(_) = self.ref_tracking {
|
||||
let ptr = value.to_pointer(self.ecx)?;
|
||||
let ptr = scalar.to_pointer(self.ecx)?;
|
||||
let _fn = try_validation!(
|
||||
self.ecx.get_ptr_fn(ptr),
|
||||
self.path,
|
||||
|
|
@ -631,10 +747,18 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
// FIXME: Check if the signature matches
|
||||
} else {
|
||||
// Otherwise (for standalone Miri), we have to still check it to be non-null.
|
||||
if self.ecx.scalar_may_be_null(value)? {
|
||||
if self.ecx.scalar_may_be_null(scalar)? {
|
||||
throw_validation_failure!(self.path, NullFnPtr);
|
||||
}
|
||||
}
|
||||
if self.reset_provenance_and_padding {
|
||||
// Make sure we do not preserve partial provenance. This matches the thin
|
||||
// pointer handling in `deref_pointer`.
|
||||
if matches!(scalar, Scalar::Int(..)) {
|
||||
self.ecx.clear_provenance(value)?;
|
||||
}
|
||||
self.add_data_range_place(value);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
ty::Never => throw_validation_failure!(self.path, NeverVal),
|
||||
|
|
@ -716,13 +840,178 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool {
|
||||
if let Some(mplace) = op.as_mplace_or_imm().left() {
|
||||
fn in_mutable_memory(&self, val: &PlaceTy<'tcx, M::Provenance>) -> bool {
|
||||
if let Some(mplace) = val.as_mplace_or_local().left() {
|
||||
if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
|
||||
return mutability(self.ecx, alloc_id).is_mut();
|
||||
mutability(self.ecx, alloc_id).is_mut()
|
||||
} else {
|
||||
// No memory at all.
|
||||
false
|
||||
}
|
||||
} else {
|
||||
// A local variable -- definitely mutable.
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the given pointer-length pair to the "data" range of this visit.
|
||||
fn add_data_range(&mut self, ptr: Pointer<Option<M::Provenance>>, size: Size) {
|
||||
if let Some(data_bytes) = self.data_bytes.as_mut() {
|
||||
// We only have to store the offset, the rest is the same for all pointers here.
|
||||
let (_prov, offset) = ptr.into_parts();
|
||||
// Add this.
|
||||
data_bytes.add_range(offset, size);
|
||||
};
|
||||
}
|
||||
|
||||
/// Add the entire given place to the "data" range of this visit.
|
||||
fn add_data_range_place(&mut self, place: &PlaceTy<'tcx, M::Provenance>) {
|
||||
// Only sized places can be added this way.
|
||||
debug_assert!(place.layout.abi.is_sized());
|
||||
if let Some(data_bytes) = self.data_bytes.as_mut() {
|
||||
let offset = Self::data_range_offset(self.ecx, place);
|
||||
data_bytes.add_range(offset, place.layout.size);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a place into the offset it starts at, for the purpose of data_range tracking.
|
||||
/// Must only be called if `data_bytes` is `Some(_)`.
|
||||
fn data_range_offset(ecx: &InterpCx<'tcx, M>, place: &PlaceTy<'tcx, M::Provenance>) -> Size {
|
||||
// The presence of `data_bytes` implies that our place is in memory.
|
||||
let ptr = ecx
|
||||
.place_to_op(place)
|
||||
.expect("place must be in memory")
|
||||
.as_mplace_or_imm()
|
||||
.expect_left("place must be in memory")
|
||||
.ptr();
|
||||
let (_prov, offset) = ptr.into_parts();
|
||||
offset
|
||||
}
|
||||
|
||||
fn reset_padding(&mut self, place: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
let Some(data_bytes) = self.data_bytes.as_mut() else { return Ok(()) };
|
||||
// Our value must be in memory, otherwise we would not have set up `data_bytes`.
|
||||
let mplace = self.ecx.force_allocation(place)?;
|
||||
// Determine starting offset and size.
|
||||
let (_prov, start_offset) = mplace.ptr().into_parts();
|
||||
let (size, _align) = self
|
||||
.ecx
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// If there is no padding at all, we can skip the rest: check for
|
||||
// a single data range covering the entire value.
|
||||
if data_bytes.0 == &[(start_offset, size)] {
|
||||
return Ok(());
|
||||
}
|
||||
// Get a handle for the allocation. Do this only once, to avoid looking up the same
|
||||
// allocation over and over again. (Though to be fair, iterating the value already does
|
||||
// exactly that.)
|
||||
let Some(mut alloc) = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)? else {
|
||||
// A ZST, no padding to clear.
|
||||
return Ok(());
|
||||
};
|
||||
// Add a "finalizer" data range at the end, so that the iteration below finds all gaps
|
||||
// between ranges.
|
||||
data_bytes.0.push((start_offset + size, Size::ZERO));
|
||||
// Iterate, and reset gaps.
|
||||
let mut padding_cleared_until = start_offset;
|
||||
for &(offset, size) in data_bytes.0.iter() {
|
||||
assert!(
|
||||
offset >= padding_cleared_until,
|
||||
"reset_padding on {}: previous field ended at offset {}, next field starts at {} (and has a size of {} bytes)",
|
||||
mplace.layout.ty,
|
||||
(padding_cleared_until - start_offset).bytes(),
|
||||
(offset - start_offset).bytes(),
|
||||
size.bytes(),
|
||||
);
|
||||
if offset > padding_cleared_until {
|
||||
// We found padding. Adjust the range to be relative to `alloc`, and make it uninit.
|
||||
let padding_start = padding_cleared_until - start_offset;
|
||||
let padding_size = offset - padding_cleared_until;
|
||||
let range = alloc_range(padding_start, padding_size);
|
||||
trace!("reset_padding on {}: resetting padding range {range:?}", mplace.layout.ty);
|
||||
alloc.write_uninit(range)?;
|
||||
}
|
||||
padding_cleared_until = offset + size;
|
||||
}
|
||||
assert!(padding_cleared_until == start_offset + size);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Computes the data range of this union type:
|
||||
/// which bytes are inside a field (i.e., not padding.)
|
||||
fn union_data_range<'e>(
|
||||
ecx: &'e mut InterpCx<'tcx, M>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> Cow<'e, RangeSet> {
|
||||
assert!(layout.ty.is_union());
|
||||
assert!(layout.abi.is_sized(), "there are no unsized unions");
|
||||
let layout_cx = LayoutCx { tcx: *ecx.tcx, param_env: ecx.param_env };
|
||||
return M::cached_union_data_range(ecx, layout.ty, || {
|
||||
let mut out = RangeSet(Vec::new());
|
||||
union_data_range_uncached(&layout_cx, layout, Size::ZERO, &mut out);
|
||||
out
|
||||
});
|
||||
|
||||
/// Helper for recursive traversal: add data ranges of the given type to `out`.
|
||||
fn union_data_range_uncached<'tcx>(
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
base_offset: Size,
|
||||
out: &mut RangeSet,
|
||||
) {
|
||||
// If this is a ZST, we don't contain any data. In particular, this helps us to quickly
|
||||
// skip over huge arrays of ZST.
|
||||
if layout.is_zst() {
|
||||
return;
|
||||
}
|
||||
// Just recursively add all the fields of everything to the output.
|
||||
match &layout.fields {
|
||||
FieldsShape::Primitive => {
|
||||
out.add_range(base_offset, layout.size);
|
||||
}
|
||||
&FieldsShape::Union(fields) => {
|
||||
// Currently, all fields start at offset 0 (relative to `base_offset`).
|
||||
for field in 0..fields.get() {
|
||||
let field = layout.field(cx, field);
|
||||
union_data_range_uncached(cx, field, base_offset, out);
|
||||
}
|
||||
}
|
||||
&FieldsShape::Array { stride, count } => {
|
||||
let elem = layout.field(cx, 0);
|
||||
|
||||
// Fast-path for large arrays of simple types that do not contain any padding.
|
||||
if elem.abi.is_scalar() {
|
||||
out.add_range(base_offset, elem.size * count);
|
||||
} else {
|
||||
for idx in 0..count {
|
||||
// This repeats the same computation for every array element... but the alternative
|
||||
// is to allocate temporary storage for a dedicated `out` set for the array element,
|
||||
// and replicating that N times. Is that better?
|
||||
union_data_range_uncached(cx, elem, base_offset + idx * stride, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
FieldsShape::Arbitrary { offsets, .. } => {
|
||||
for (field, &offset) in offsets.iter_enumerated() {
|
||||
let field = layout.field(cx, field.as_usize());
|
||||
union_data_range_uncached(cx, field, base_offset + offset, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't forget potential other variants.
|
||||
match &layout.variants {
|
||||
Variants::Single { .. } => {
|
||||
// Fully handled above.
|
||||
}
|
||||
Variants::Multiple { variants, .. } => {
|
||||
for variant in variants.indices() {
|
||||
let variant = layout.for_variant(cx, variant);
|
||||
union_data_range_uncached(cx, variant, base_offset, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -774,7 +1063,7 @@ fn mutability<'tcx>(ecx: &InterpCx<'tcx, impl Machine<'tcx>>, alloc_id: AllocId)
|
|||
}
|
||||
|
||||
impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, 'tcx, M> {
|
||||
type V = OpTy<'tcx, M::Provenance>;
|
||||
type V = PlaceTy<'tcx, M::Provenance>;
|
||||
|
||||
#[inline(always)]
|
||||
fn ecx(&self) -> &InterpCx<'tcx, M> {
|
||||
|
|
@ -783,11 +1072,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
|
||||
fn read_discriminant(
|
||||
&mut self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, VariantIdx> {
|
||||
self.with_elem(PathElem::EnumTag, move |this| {
|
||||
Ok(try_validation!(
|
||||
this.ecx.read_discriminant(op),
|
||||
this.ecx.read_discriminant(val),
|
||||
this.path,
|
||||
Ub(InvalidTag(val)) => InvalidEnumTag {
|
||||
value: format!("{val:x}"),
|
||||
|
|
@ -802,44 +1091,54 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
#[inline]
|
||||
fn visit_field(
|
||||
&mut self,
|
||||
old_op: &OpTy<'tcx, M::Provenance>,
|
||||
old_val: &PlaceTy<'tcx, M::Provenance>,
|
||||
field: usize,
|
||||
new_op: &OpTy<'tcx, M::Provenance>,
|
||||
new_val: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let elem = self.aggregate_field_path_elem(old_op.layout, field);
|
||||
self.with_elem(elem, move |this| this.visit_value(new_op))
|
||||
let elem = self.aggregate_field_path_elem(old_val.layout, field);
|
||||
self.with_elem(elem, move |this| this.visit_value(new_val))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_variant(
|
||||
&mut self,
|
||||
old_op: &OpTy<'tcx, M::Provenance>,
|
||||
old_val: &PlaceTy<'tcx, M::Provenance>,
|
||||
variant_id: VariantIdx,
|
||||
new_op: &OpTy<'tcx, M::Provenance>,
|
||||
new_val: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let name = match old_op.layout.ty.kind() {
|
||||
let name = match old_val.layout.ty.kind() {
|
||||
ty::Adt(adt, _) => PathElem::Variant(adt.variant(variant_id).name),
|
||||
// Coroutines also have variants
|
||||
ty::Coroutine(..) => PathElem::CoroutineState(variant_id),
|
||||
_ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty),
|
||||
_ => bug!("Unexpected type with variant: {:?}", old_val.layout.ty),
|
||||
};
|
||||
self.with_elem(name, move |this| this.visit_value(new_op))
|
||||
self.with_elem(name, move |this| this.visit_value(new_val))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_union(
|
||||
&mut self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
_fields: NonZero<usize>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory.
|
||||
if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) {
|
||||
if !op.layout.is_zst() && !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) {
|
||||
if !self.in_mutable_memory(op) {
|
||||
if !val.layout.is_zst() && !val.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) {
|
||||
if !self.in_mutable_memory(val) {
|
||||
throw_validation_failure!(self.path, UnsafeCellInImmutable);
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.reset_provenance_and_padding
|
||||
&& let Some(data_bytes) = self.data_bytes.as_mut()
|
||||
{
|
||||
let base_offset = Self::data_range_offset(self.ecx, val);
|
||||
// Determine and add data range for this union.
|
||||
let union_data_range = Self::union_data_range(self.ecx, val.layout);
|
||||
for &(offset, size) in union_data_range.0.iter() {
|
||||
data_bytes.add_range(base_offset + offset, size);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -847,39 +1146,41 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
fn visit_box(
|
||||
&mut self,
|
||||
_box_ty: Ty<'tcx>,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.check_safe_pointer(op, PointerKind::Box)?;
|
||||
self.check_safe_pointer(val, PointerKind::Box)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_value(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
trace!("visit_value: {:?}, {:?}", *op, op.layout);
|
||||
fn visit_value(&mut self, val: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
trace!("visit_value: {:?}, {:?}", *val, val.layout);
|
||||
|
||||
// Check primitive types -- the leaves of our recursive descent.
|
||||
// This is called even for enum discriminants (which are "fields" of their enum),
|
||||
// so for integer-typed discriminants the provenance reset will happen here.
|
||||
// We assume that the Scalar validity range does not restrict these values
|
||||
// any further than `try_visit_primitive` does!
|
||||
if self.try_visit_primitive(op)? {
|
||||
if self.try_visit_primitive(val)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Special check preventing `UnsafeCell` in the inner part of constants
|
||||
if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) {
|
||||
if !op.layout.is_zst()
|
||||
&& let Some(def) = op.layout.ty.ty_adt_def()
|
||||
if !val.layout.is_zst()
|
||||
&& let Some(def) = val.layout.ty.ty_adt_def()
|
||||
&& def.is_unsafe_cell()
|
||||
{
|
||||
if !self.in_mutable_memory(op) {
|
||||
if !self.in_mutable_memory(val) {
|
||||
throw_validation_failure!(self.path, UnsafeCellInImmutable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively walk the value at its type. Apply optimizations for some large types.
|
||||
match op.layout.ty.kind() {
|
||||
match val.layout.ty.kind() {
|
||||
ty::Str => {
|
||||
let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate
|
||||
let mplace = val.assert_mem_place(); // strings are unsized and hence never immediate
|
||||
let len = mplace.len(self.ecx)?;
|
||||
try_validation!(
|
||||
self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len)),
|
||||
|
|
@ -889,11 +1190,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
);
|
||||
}
|
||||
ty::Array(tys, ..) | ty::Slice(tys)
|
||||
// This optimization applies for types that can hold arbitrary bytes (such as
|
||||
// integer and floating point types) or for structs or tuples with no fields.
|
||||
// FIXME(wesleywiser) This logic could be extended further to arbitrary structs
|
||||
// or tuples made up of integer/floating point types or inhabited ZSTs with no
|
||||
// padding.
|
||||
// This optimization applies for types that can hold arbitrary non-provenance bytes (such as
|
||||
// integer and floating point types).
|
||||
// FIXME(wesleywiser) This logic could be extended further to arbitrary structs or
|
||||
// tuples made up of integer/floating point types or inhabited ZSTs with no padding.
|
||||
if matches!(tys.kind(), ty::Int(..) | ty::Uint(..) | ty::Float(..))
|
||||
=>
|
||||
{
|
||||
|
|
@ -901,18 +1201,19 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
// Optimized handling for arrays of integer/float type.
|
||||
|
||||
// This is the length of the array/slice.
|
||||
let len = op.len(self.ecx)?;
|
||||
let len = val.len(self.ecx)?;
|
||||
// This is the element type size.
|
||||
let layout = self.ecx.layout_of(*tys)?;
|
||||
// This is the size in bytes of the whole array. (This checks for overflow.)
|
||||
let size = layout.size * len;
|
||||
// If the size is 0, there is nothing to check.
|
||||
// (`size` can only be 0 of `len` is 0, and empty arrays are always valid.)
|
||||
// (`size` can only be 0 if `len` is 0, and empty arrays are always valid.)
|
||||
if size == Size::ZERO {
|
||||
return Ok(());
|
||||
}
|
||||
// Now that we definitely have a non-ZST array, we know it lives in memory.
|
||||
let mplace = match op.as_mplace_or_imm() {
|
||||
// Now that we definitely have a non-ZST array, we know it lives in memory -- except it may
|
||||
// be an uninitialized local variable, those are also "immediate".
|
||||
let mplace = match val.to_op(self.ecx)?.as_mplace_or_imm() {
|
||||
Left(mplace) => mplace,
|
||||
Right(imm) => match *imm {
|
||||
Immediate::Uninit =>
|
||||
|
|
@ -958,20 +1259,30 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't forget that these are all non-pointer types, and thus do not preserve
|
||||
// provenance.
|
||||
if self.reset_provenance_and_padding {
|
||||
// We can't share this with above as above, we might be looking at read-only memory.
|
||||
let mut alloc = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)?.expect("we already excluded size 0");
|
||||
alloc.clear_provenance()?;
|
||||
// Also, mark this as containing data, not padding.
|
||||
self.add_data_range(mplace.ptr(), size);
|
||||
}
|
||||
}
|
||||
// Fast path for arrays and slices of ZSTs. We only need to check a single ZST element
|
||||
// of an array and not all of them, because there's only a single value of a specific
|
||||
// ZST type, so either validation fails for all elements or none.
|
||||
ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => {
|
||||
// Validate just the first element (if any).
|
||||
if op.len(self.ecx)? > 0 {
|
||||
self.visit_field(op, 0, &self.ecx.project_index(op, 0)?)?;
|
||||
if val.len(self.ecx)? > 0 {
|
||||
self.visit_field(val, 0, &self.ecx.project_index(val, 0)?)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// default handler
|
||||
try_validation!(
|
||||
self.walk_value(op),
|
||||
self.walk_value(val),
|
||||
self.path,
|
||||
// It's not great to catch errors here, since we can't give a very good path,
|
||||
// but it's better than ICEing.
|
||||
|
|
@ -992,15 +1303,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
|
||||
// scalars, we do the same check on every "level" (e.g., first we check
|
||||
// MyNewtype and then the scalar in there).
|
||||
match op.layout.abi {
|
||||
match val.layout.abi {
|
||||
Abi::Uninhabited => {
|
||||
let ty = op.layout.ty;
|
||||
let ty = val.layout.ty;
|
||||
throw_validation_failure!(self.path, UninhabitedVal { ty });
|
||||
}
|
||||
Abi::Scalar(scalar_layout) => {
|
||||
if !scalar_layout.is_uninit_valid() {
|
||||
// There is something to check here.
|
||||
let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
|
||||
let scalar = self.read_scalar(val, ExpectedKind::InitScalar)?;
|
||||
self.visit_scalar(scalar, scalar_layout)?;
|
||||
}
|
||||
}
|
||||
|
|
@ -1010,7 +1321,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
// the other must be init.
|
||||
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
|
||||
let (a, b) =
|
||||
self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
|
||||
self.read_immediate(val, ExpectedKind::InitScalar)?.to_scalar_pair();
|
||||
self.visit_scalar(a, a_layout)?;
|
||||
self.visit_scalar(b, b_layout)?;
|
||||
}
|
||||
|
|
@ -1031,19 +1342,34 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
fn validate_operand_internal(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
&mut self,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
path: Vec<PathElem>,
|
||||
ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>,
|
||||
ctfe_mode: Option<CtfeValidationMode>,
|
||||
reset_provenance_and_padding: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty);
|
||||
trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty);
|
||||
|
||||
// Construct a visitor
|
||||
let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx: self };
|
||||
|
||||
// Run it.
|
||||
match self.run_for_validation(|| visitor.visit_value(op)) {
|
||||
// Run the visitor.
|
||||
match self.run_for_validation(|ecx| {
|
||||
let reset_padding = reset_provenance_and_padding && {
|
||||
// Check if `val` is actually stored in memory. If not, padding is not even
|
||||
// represented and we need not reset it.
|
||||
ecx.place_to_op(val)?.as_mplace_or_imm().is_left()
|
||||
};
|
||||
let mut v = ValidityVisitor {
|
||||
path,
|
||||
ref_tracking,
|
||||
ctfe_mode,
|
||||
ecx,
|
||||
reset_provenance_and_padding,
|
||||
data_bytes: reset_padding.then_some(RangeSet(Vec::new())),
|
||||
};
|
||||
v.visit_value(val)?;
|
||||
v.reset_padding(val)?;
|
||||
InterpResult::Ok(())
|
||||
}) {
|
||||
Ok(()) => Ok(()),
|
||||
// Pass through validation failures and "invalid program" issues.
|
||||
Err(err)
|
||||
|
|
@ -1079,13 +1405,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// - no `UnsafeCell` or non-ZST `&mut`.
|
||||
#[inline(always)]
|
||||
pub(crate) fn const_validate_operand(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
&mut self,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
path: Vec<PathElem>,
|
||||
ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>,
|
||||
ctfe_mode: CtfeValidationMode,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode))
|
||||
self.validate_operand_internal(
|
||||
val,
|
||||
path,
|
||||
Some(ref_tracking),
|
||||
Some(ctfe_mode),
|
||||
/*reset_provenance*/ false,
|
||||
)
|
||||
}
|
||||
|
||||
/// This function checks the data at `op` to be runtime-valid.
|
||||
|
|
@ -1093,21 +1425,41 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// It will error if the bits at the destination do not match the ones described by the layout.
|
||||
#[inline(always)]
|
||||
pub fn validate_operand(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
&mut self,
|
||||
val: &PlaceTy<'tcx, M::Provenance>,
|
||||
recursive: bool,
|
||||
reset_provenance_and_padding: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Note that we *could* actually be in CTFE here with `-Zextra-const-ub-checks`, but it's
|
||||
// still correct to not use `ctfe_mode`: that mode is for validation of the final constant
|
||||
// value, it rules out things like `UnsafeCell` in awkward places.
|
||||
if !recursive {
|
||||
return self.validate_operand_internal(op, vec![], None, None);
|
||||
return self.validate_operand_internal(
|
||||
val,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
reset_provenance_and_padding,
|
||||
);
|
||||
}
|
||||
// Do a recursive check.
|
||||
let mut ref_tracking = RefTracking::empty();
|
||||
self.validate_operand_internal(op, vec![], Some(&mut ref_tracking), None)?;
|
||||
self.validate_operand_internal(
|
||||
val,
|
||||
vec![],
|
||||
Some(&mut ref_tracking),
|
||||
None,
|
||||
reset_provenance_and_padding,
|
||||
)?;
|
||||
while let Some((mplace, path)) = ref_tracking.todo.pop() {
|
||||
self.validate_operand_internal(&mplace.into(), path, Some(&mut ref_tracking), None)?;
|
||||
// Things behind reference do *not* have the provenance reset.
|
||||
self.validate_operand_internal(
|
||||
&mplace.into(),
|
||||
path,
|
||||
Some(&mut ref_tracking),
|
||||
None,
|
||||
/*reset_provenance_and_padding*/ false,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::num::NonZero;
|
|||
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::interpret::InterpResult;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_target::abi::{FieldIdx, FieldsShape, VariantIdx, Variants};
|
||||
use tracing::trace;
|
||||
|
|
@ -82,6 +83,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
|
|||
self.visit_value(new_val)
|
||||
}
|
||||
|
||||
/// Traversal logic; should not be overloaded.
|
||||
fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> {
|
||||
let ty = v.layout().ty;
|
||||
trace!("walk_value: type: {ty}");
|
||||
|
|
@ -104,6 +106,17 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
|
|||
// DynStar types. Very different from a dyn type (but strangely part of the
|
||||
// same variant in `TyKind`): These are pairs where the 2nd component is the
|
||||
// vtable, and the first component is the data (which must be ptr-sized).
|
||||
|
||||
// First make sure the vtable can be read at its type.
|
||||
// The type of this vtable is fake, it claims to be a reference to some actual memory but that isn't true.
|
||||
// So we transmute it to a raw pointer.
|
||||
let raw_ptr_ty = Ty::new_mut_ptr(*self.ecx().tcx, self.ecx().tcx.types.unit);
|
||||
let raw_ptr_ty = self.ecx().layout_of(raw_ptr_ty)?;
|
||||
let vtable_field =
|
||||
self.ecx().project_field(v, 1)?.transmute(raw_ptr_ty, self.ecx())?;
|
||||
self.visit_field(v, 1, &vtable_field)?;
|
||||
|
||||
// Then unpack the first field, and continue.
|
||||
let data = self.ecx().unpack_dyn_star(v, data)?;
|
||||
return self.visit_field(v, 0, &data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
|
|||
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
|
||||
|
||||
use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine};
|
||||
use crate::interpret::{InterpCx, MemoryKind, OpTy};
|
||||
use crate::interpret::{InterpCx, MemoryKind};
|
||||
|
||||
/// Determines if this type permits "raw" initialization by just transmuting some memory into an
|
||||
/// instance of `T`.
|
||||
|
|
@ -32,15 +32,15 @@ pub fn check_validity_requirement<'tcx>(
|
|||
|
||||
let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env };
|
||||
if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks {
|
||||
might_permit_raw_init_strict(layout, &layout_cx, kind)
|
||||
check_validity_requirement_strict(layout, &layout_cx, kind)
|
||||
} else {
|
||||
might_permit_raw_init_lax(layout, &layout_cx, kind)
|
||||
check_validity_requirement_lax(layout, &layout_cx, kind)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements the 'strict' version of the `might_permit_raw_init` checks; see that function for
|
||||
/// details.
|
||||
fn might_permit_raw_init_strict<'tcx>(
|
||||
/// Implements the 'strict' version of the [`check_validity_requirement`] checks; see that function
|
||||
/// for details.
|
||||
fn check_validity_requirement_strict<'tcx>(
|
||||
ty: TyAndLayout<'tcx>,
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
kind: ValidityRequirement,
|
||||
|
|
@ -61,18 +61,24 @@ fn might_permit_raw_init_strict<'tcx>(
|
|||
.expect("failed to write bytes for zero valid check");
|
||||
}
|
||||
|
||||
let ot: OpTy<'_, _> = allocated.into();
|
||||
|
||||
// Assume that if it failed, it's a validation failure.
|
||||
// This does *not* actually check that references are dereferenceable, but since all types that
|
||||
// require dereferenceability also require non-null, we don't actually get any false negatives
|
||||
// due to this.
|
||||
Ok(cx.validate_operand(&ot, /*recursive*/ false).is_ok())
|
||||
// The value we are validating is temporary and discarded at the end of this function, so
|
||||
// there is no point in reseting provenance and padding.
|
||||
Ok(cx
|
||||
.validate_operand(
|
||||
&allocated.into(),
|
||||
/*recursive*/ false,
|
||||
/*reset_provenance_and_padding*/ false,
|
||||
)
|
||||
.is_ok())
|
||||
}
|
||||
|
||||
/// Implements the 'lax' (default) version of the `might_permit_raw_init` checks; see that function for
|
||||
/// details.
|
||||
fn might_permit_raw_init_lax<'tcx>(
|
||||
/// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that
|
||||
/// function for details.
|
||||
fn check_validity_requirement_lax<'tcx>(
|
||||
this: TyAndLayout<'tcx>,
|
||||
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
init_kind: ValidityRequirement,
|
||||
|
|
@ -137,7 +143,7 @@ fn might_permit_raw_init_lax<'tcx>(
|
|||
}
|
||||
FieldsShape::Arbitrary { offsets, .. } => {
|
||||
for idx in 0..offsets.len() {
|
||||
if !might_permit_raw_init_lax(this.field(cx, idx), cx, init_kind)? {
|
||||
if !check_validity_requirement_lax(this.field(cx, idx), cx, init_kind)? {
|
||||
// We found a field that is unhappy with this kind of initialization.
|
||||
return Ok(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ impl<T> Steal<T> {
|
|||
///
|
||||
/// This should not be used within rustc as it leaks information not tracked
|
||||
/// by the query system, breaking incremental compilation.
|
||||
#[cfg_attr(not(bootstrap), rustc_lint_untracked_query_information)]
|
||||
pub fn is_stolen(&self) -> bool {
|
||||
self.value.borrow().is_none()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
|
||||
#![cfg_attr(bootstrap, feature(unsafe_extern_blocks))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(decl_macro)]
|
||||
|
|
@ -862,9 +861,9 @@ fn print_crate_info(
|
|||
use rustc_target::spec::current_apple_deployment_target;
|
||||
|
||||
if sess.target.is_like_osx {
|
||||
let (major, minor) = current_apple_deployment_target(&sess.target)
|
||||
.expect("unknown Apple target OS");
|
||||
println_info!("deployment_target={}", format!("{major}.{minor}"))
|
||||
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
|
||||
let patch = if patch != 0 { format!(".{patch}") } else { String::new() };
|
||||
println_info!("deployment_target={major}.{minor}{patch}")
|
||||
} else {
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
sess.dcx().fatal("only Apple targets currently support deployment version info")
|
||||
|
|
|
|||
|
|
@ -398,8 +398,10 @@ pub(crate) enum NamedMatch {
|
|||
fn token_name_eq(t1: &Token, t2: &Token) -> bool {
|
||||
if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) = (t1.ident(), t2.ident()) {
|
||||
ident1.name == ident2.name && is_raw1 == is_raw2
|
||||
} else if let (Some(ident1), Some(ident2)) = (t1.lifetime(), t2.lifetime()) {
|
||||
ident1.name == ident2.name
|
||||
} else if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) =
|
||||
(t1.lifetime(), t2.lifetime())
|
||||
{
|
||||
ident1.name == ident2.name && is_raw1 == is_raw2
|
||||
} else {
|
||||
t1.kind == t2.kind
|
||||
}
|
||||
|
|
|
|||
|
|
@ -283,9 +283,9 @@ pub(super) fn transcribe<'a>(
|
|||
let kind = token::NtIdent(*ident, *is_raw);
|
||||
TokenTree::token_alone(kind, sp)
|
||||
}
|
||||
MatchedSingle(ParseNtResult::Lifetime(ident)) => {
|
||||
MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
|
||||
marker.visit_span(&mut sp);
|
||||
let kind = token::NtLifetime(*ident);
|
||||
let kind = token::NtLifetime(*ident, *is_raw);
|
||||
TokenTree::token_alone(kind, sp)
|
||||
}
|
||||
MatchedSingle(ParseNtResult::Nt(nt)) => {
|
||||
|
|
|
|||
|
|
@ -229,15 +229,16 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
|
|||
span: ident.span,
|
||||
})),
|
||||
|
||||
Lifetime(name) => {
|
||||
Lifetime(name, is_raw) => {
|
||||
let ident = symbol::Ident::new(name, span).without_first_quote();
|
||||
trees.extend([
|
||||
TokenTree::Punct(Punct { ch: b'\'', joint: true, span }),
|
||||
TokenTree::Ident(Ident { sym: ident.name, is_raw: false, span }),
|
||||
TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }),
|
||||
]);
|
||||
}
|
||||
NtLifetime(ident) => {
|
||||
let stream = TokenStream::token_alone(token::Lifetime(ident.name), ident.span);
|
||||
NtLifetime(ident, is_raw) => {
|
||||
let stream =
|
||||
TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span);
|
||||
trees.push(TokenTree::Group(Group {
|
||||
delimiter: pm::Delimiter::None,
|
||||
stream: Some(stream),
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ declare_features! (
|
|||
/// Allows explicit discriminants on non-unit enum variants.
|
||||
(accepted, arbitrary_enum_discriminant, "1.66.0", Some(60553)),
|
||||
/// Allows using `const` operands in inline assembly.
|
||||
(accepted, asm_const, "CURRENT_RUSTC_VERSION", Some(93332)),
|
||||
(accepted, asm_const, "1.82.0", Some(93332)),
|
||||
/// Allows using `sym` operands in inline assembly.
|
||||
(accepted, asm_sym, "1.66.0", Some(93333)),
|
||||
/// Allows the definition of associated constants in `trait` or `impl` blocks.
|
||||
|
|
@ -116,7 +116,7 @@ declare_features! (
|
|||
/// Allows calling constructor functions in `const fn`.
|
||||
(accepted, const_constructor, "1.40.0", Some(61456)),
|
||||
/// Allows basic arithmetic on floating point types in a `const fn`.
|
||||
(accepted, const_fn_floating_point_arithmetic, "CURRENT_RUSTC_VERSION", Some(57241)),
|
||||
(accepted, const_fn_floating_point_arithmetic, "1.82.0", Some(57241)),
|
||||
/// Allows using and casting function pointers in a `const fn`.
|
||||
(accepted, const_fn_fn_ptr_basics, "1.61.0", Some(57563)),
|
||||
/// Allows trait bounds in `const fn`.
|
||||
|
|
@ -272,7 +272,7 @@ declare_features! (
|
|||
/// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions.
|
||||
(accepted, min_const_unsafe_fn, "1.33.0", Some(55607)),
|
||||
/// Allows exhaustive pattern matching on uninhabited types when matched by value.
|
||||
(accepted, min_exhaustive_patterns, "CURRENT_RUSTC_VERSION", Some(119612)),
|
||||
(accepted, min_exhaustive_patterns, "1.82.0", Some(119612)),
|
||||
/// Allows using `Self` and associated types in struct expressions and patterns.
|
||||
(accepted, more_struct_aliases, "1.16.0", Some(37544)),
|
||||
/// Allows using the MOVBE target feature.
|
||||
|
|
@ -299,7 +299,7 @@ declare_features! (
|
|||
/// Allows `foo.rs` as an alternative to `foo/mod.rs`.
|
||||
(accepted, non_modrs_mods, "1.30.0", Some(44660)),
|
||||
/// Allows using multiple nested field accesses in offset_of!
|
||||
(accepted, offset_of_nested, "CURRENT_RUSTC_VERSION", Some(120140)),
|
||||
(accepted, offset_of_nested, "1.82.0", Some(120140)),
|
||||
/// Allows the use of or-patterns (e.g., `0 | 1`).
|
||||
(accepted, or_patterns, "1.53.0", Some(54883)),
|
||||
/// Allows using `+bundle,+whole-archive` link modifiers with native libs.
|
||||
|
|
@ -312,7 +312,7 @@ declare_features! (
|
|||
/// Allows parentheses in patterns.
|
||||
(accepted, pattern_parentheses, "1.31.0", Some(51087)),
|
||||
/// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args.
|
||||
(accepted, precise_capturing, "CURRENT_RUSTC_VERSION", Some(123432)),
|
||||
(accepted, precise_capturing, "1.82.0", Some(123432)),
|
||||
/// Allows procedural macros in `proc-macro` crates.
|
||||
(accepted, proc_macro, "1.29.0", Some(38356)),
|
||||
/// Allows multi-segment paths in attributes and derives.
|
||||
|
|
@ -326,7 +326,7 @@ declare_features! (
|
|||
/// Allows keywords to be escaped for use as identifiers.
|
||||
(accepted, raw_identifiers, "1.30.0", Some(48589)),
|
||||
/// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
|
||||
(accepted, raw_ref_op, "CURRENT_RUSTC_VERSION", Some(64490)),
|
||||
(accepted, raw_ref_op, "1.82.0", Some(64490)),
|
||||
/// Allows relaxing the coherence rules such that
|
||||
/// `impl<T> ForeignTrait<LocalType> for ForeignType<T>` is permitted.
|
||||
(accepted, re_rebalance_coherence, "1.41.0", Some(55437)),
|
||||
|
|
@ -399,11 +399,11 @@ declare_features! (
|
|||
/// Allows arbitrary delimited token streams in non-macro attributes.
|
||||
(accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208)),
|
||||
/// Allows unsafe attributes.
|
||||
(accepted, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)),
|
||||
(accepted, unsafe_attributes, "1.82.0", Some(123757)),
|
||||
/// The `unsafe_op_in_unsafe_fn` lint (allowed by default): no longer treat an unsafe function as an unsafe block.
|
||||
(accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668)),
|
||||
/// Allows unsafe on extern declarations and safety qualifiers over internal items.
|
||||
(accepted, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)),
|
||||
(accepted, unsafe_extern_blocks, "1.82.0", Some(123743)),
|
||||
/// Allows importing and reexporting macros with `use`,
|
||||
/// enables macro modularization in general.
|
||||
(accepted, use_extern_macros, "1.30.0", Some(35896)),
|
||||
|
|
|
|||
|
|
@ -793,6 +793,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
rustc_lint_query_instability, Normal, template!(Word),
|
||||
WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
|
||||
),
|
||||
// Used by the `rustc::untracked_query_information` lint to warn methods which
|
||||
// might not be stable during incremental compilation.
|
||||
rustc_attr!(
|
||||
rustc_lint_untracked_query_information, Normal, template!(Word),
|
||||
WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
|
||||
),
|
||||
// Used by the `rustc::diagnostic_outside_of_impl` lints to assist in changes to diagnostic
|
||||
// APIs. Any function with this attribute will be checked by that lint.
|
||||
rustc_attr!(
|
||||
|
|
@ -1180,3 +1186,11 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>>
|
|||
}
|
||||
map
|
||||
});
|
||||
|
||||
pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool {
|
||||
match sym {
|
||||
sym::on_unimplemented => true,
|
||||
sym::do_not_recommend => features.do_not_recommend,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,8 +130,9 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u
|
|||
pub use accepted::ACCEPTED_FEATURES;
|
||||
pub use builtin_attrs::{
|
||||
deprecated_attributes, encode_cross_crate, find_gated_cfg, is_builtin_attr_name,
|
||||
is_valid_for_get_attr, AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate,
|
||||
AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
|
||||
is_stable_diagnostic_attribute, is_valid_for_get_attr, AttributeDuplicates, AttributeGate,
|
||||
AttributeSafety, AttributeTemplate, AttributeType, BuiltinAttribute, GatedCfg,
|
||||
BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
|
||||
};
|
||||
pub use removed::REMOVED_FEATURES;
|
||||
pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_FEATURES};
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ declare_features! (
|
|||
(removed, custom_derive, "1.32.0", Some(29644),
|
||||
Some("subsumed by `#[proc_macro_derive]`")),
|
||||
/// Allows default type parameters to influence type inference.
|
||||
(removed, default_type_parameter_fallback, "CURRENT_RUSTC_VERSION", Some(27336),
|
||||
(removed, default_type_parameter_fallback, "1.82.0", Some(27336),
|
||||
Some("never properly implemented; requires significant design work")),
|
||||
/// Allows using `#[doc(keyword = "...")]`.
|
||||
(removed, doc_keyword, "1.28.0", Some(51315),
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ declare_features! (
|
|||
/// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406
|
||||
(unstable, link_cfg, "1.14.0", None),
|
||||
/// Allows using `?Trait` trait bounds in more contexts.
|
||||
(internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None),
|
||||
(internal, more_maybe_bounds, "1.82.0", None),
|
||||
/// Allows the `multiple_supertrait_upcastable` lint.
|
||||
(unstable, multiple_supertrait_upcastable, "1.69.0", None),
|
||||
/// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
|
||||
|
|
@ -302,7 +302,7 @@ declare_features! (
|
|||
// FIXME: Document these and merge with the list below.
|
||||
|
||||
// Unstable `#[target_feature]` directives.
|
||||
(unstable, aarch64_unstable_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
|
||||
(unstable, aarch64_unstable_target_feature, "1.82.0", Some(44839)),
|
||||
(unstable, aarch64_ver_target_feature, "1.27.0", Some(44839)),
|
||||
(unstable, arm_target_feature, "1.27.0", Some(44839)),
|
||||
(unstable, avx512_target_feature, "1.27.0", Some(44839)),
|
||||
|
|
@ -317,7 +317,7 @@ declare_features! (
|
|||
(unstable, prfchw_target_feature, "1.78.0", Some(44839)),
|
||||
(unstable, riscv_target_feature, "1.45.0", Some(44839)),
|
||||
(unstable, rtm_target_feature, "1.35.0", Some(44839)),
|
||||
(unstable, s390x_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
|
||||
(unstable, s390x_target_feature, "1.82.0", Some(44839)),
|
||||
(unstable, sse4a_target_feature, "1.27.0", Some(44839)),
|
||||
(unstable, tbm_target_feature, "1.27.0", Some(44839)),
|
||||
(unstable, wasm_target_feature, "1.30.0", Some(44839)),
|
||||
|
|
@ -474,7 +474,7 @@ declare_features! (
|
|||
/// Allows the use of `#[ffi_pure]` on foreign functions.
|
||||
(unstable, ffi_pure, "1.45.0", Some(58329)),
|
||||
/// Controlling the behavior of fmt::Debug
|
||||
(unstable, fmt_debug, "CURRENT_RUSTC_VERSION", Some(129709)),
|
||||
(unstable, fmt_debug, "1.82.0", Some(129709)),
|
||||
/// Allows using `#[repr(align(...))]` on function items
|
||||
(unstable, fn_align, "1.53.0", Some(82232)),
|
||||
/// Support delegating implementation of functions to other already implemented functions.
|
||||
|
|
@ -586,8 +586,8 @@ declare_features! (
|
|||
/// Allows `extern "rust-cold"`.
|
||||
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
|
||||
/// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics
|
||||
(unstable, sha512_sm_x86, "CURRENT_RUSTC_VERSION", Some(126624)),
|
||||
/// Shorten the tail expression lifetime
|
||||
(unstable, sha512_sm_x86, "1.82.0", Some(126624)),
|
||||
/// Shortern the tail expression lifetime
|
||||
(unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)),
|
||||
/// Allows the use of SIMD types in functions declared in `extern` blocks.
|
||||
(unstable, simd_ffi, "1.0.0", Some(27731)),
|
||||
|
|
@ -625,7 +625,7 @@ declare_features! (
|
|||
(incomplete, unnamed_fields, "1.74.0", Some(49804)),
|
||||
/// Allows const generic parameters to be defined with types that
|
||||
/// are not `Sized`, e.g. `fn foo<const N: [u8]>() {`.
|
||||
(incomplete, unsized_const_params, "CURRENT_RUSTC_VERSION", Some(95174)),
|
||||
(incomplete, unsized_const_params, "1.82.0", Some(95174)),
|
||||
/// Allows unsized fn parameters.
|
||||
(internal, unsized_fn_params, "1.49.0", Some(48055)),
|
||||
/// Allows unsized rvalues at arguments and parameters.
|
||||
|
|
|
|||
|
|
@ -93,7 +93,8 @@ impl<'tcx> InherentCollect<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey) {
|
||||
if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::InstantiateWithInfer)
|
||||
{
|
||||
self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
|
||||
} else {
|
||||
bug!("unexpected self type: {:?}", self_ty);
|
||||
|
|
@ -132,7 +133,7 @@ impl<'tcx> InherentCollect<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::AsCandidateKey) {
|
||||
if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::InstantiateWithInfer) {
|
||||
self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
|
||||
} else {
|
||||
bug!("unexpected primitive type: {:?}", ty);
|
||||
|
|
|
|||
|
|
@ -1698,8 +1698,6 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
|
|||
trait_ref: ty::EarlyBinder::bind(trait_ref),
|
||||
safety: impl_.safety,
|
||||
polarity: polarity_of_impl(tcx, def_id, impl_, item.span),
|
||||
do_not_recommend: tcx.features().do_not_recommend
|
||||
&& tcx.has_attrs_with_path(def_id, &[sym::diagnostic, sym::do_not_recommend]),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,12 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
|||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
|
||||
let node = tcx.hir_node(hir_id);
|
||||
if let Some(sig) = node.fn_sig()
|
||||
&& let Some(sig_id) = sig.decl.opt_delegation_sig_id()
|
||||
{
|
||||
return inherit_generics_for_delegation_item(tcx, def_id, sig_id);
|
||||
}
|
||||
|
||||
let parent_def_id = match node {
|
||||
Node::ImplItem(_)
|
||||
| Node::TraitItem(_)
|
||||
|
|
@ -229,16 +235,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
|||
// inherit the generics of the item.
|
||||
Some(parent.to_def_id())
|
||||
}
|
||||
ItemKind::Fn(sig, _, _) => {
|
||||
// For a delegation item inherit generics from callee.
|
||||
if let Some(sig_id) = sig.decl.opt_delegation_sig_id()
|
||||
&& let Some(generics) =
|
||||
inherit_generics_for_delegation_item(tcx, def_id, sig_id)
|
||||
{
|
||||
return generics;
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -138,6 +138,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
let node = tcx.hir_node(hir_id);
|
||||
|
||||
if let Some(sig) = node.fn_sig()
|
||||
&& let Some(sig_id) = sig.decl.opt_delegation_sig_id()
|
||||
{
|
||||
return inherit_predicates_for_delegation_item(tcx, def_id, sig_id);
|
||||
}
|
||||
|
||||
let mut is_trait = None;
|
||||
let mut is_default_impl_trait = None;
|
||||
|
||||
|
|
@ -164,16 +170,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
ItemKind::Trait(_, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, self_bounds) => {
|
||||
is_trait = Some(self_bounds);
|
||||
}
|
||||
|
||||
ItemKind::Fn(sig, _, _) => {
|
||||
// For a delegation item inherit predicates from callee.
|
||||
if let Some(sig_id) = sig.decl.opt_delegation_sig_id()
|
||||
&& let Some(predicates) =
|
||||
inherit_predicates_for_delegation_item(tcx, def_id, sig_id)
|
||||
{
|
||||
return predicates;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
//! Support inheriting generic parameters and predicates for function delegation.
|
||||
//!
|
||||
//! For more information about delegation design, see the tracking issue #118212.
|
||||
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
|
@ -5,7 +9,7 @@ use rustc_hir::def::DefKind;
|
|||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
use rustc_type_ir::visit::TypeVisitableExt;
|
||||
|
||||
type RemapTable = FxHashMap<u32, u32>;
|
||||
|
|
@ -76,127 +80,381 @@ fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given the current context(caller and callee `FnKind`), it specifies
|
||||
/// the policy of predicates and generic parameters inheritance.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum InheritanceKind {
|
||||
/// Copying all predicates and parameters, including those of the parent
|
||||
/// container.
|
||||
///
|
||||
/// Boolean value defines whether the `Self` parameter or `Self: Trait`
|
||||
/// predicate are copied. It's always equal to `false` except when
|
||||
/// delegating from a free function to a trait method.
|
||||
///
|
||||
/// FIXME(fn_delegation): This often leads to type inference
|
||||
/// errors. Support providing generic arguments or restrict use sites.
|
||||
WithParent(bool),
|
||||
/// The trait implementation should be compatible with the original trait.
|
||||
/// Therefore, for trait implementations only the method's own parameters
|
||||
/// and predicates are copied.
|
||||
Own,
|
||||
}
|
||||
|
||||
struct GenericsBuilder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sig_id: DefId,
|
||||
parent: Option<DefId>,
|
||||
inh_kind: InheritanceKind,
|
||||
}
|
||||
|
||||
impl<'tcx> GenericsBuilder<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, sig_id: DefId) -> GenericsBuilder<'tcx> {
|
||||
GenericsBuilder { tcx, sig_id, parent: None, inh_kind: InheritanceKind::WithParent(false) }
|
||||
}
|
||||
|
||||
fn with_parent(mut self, parent: DefId) -> Self {
|
||||
self.parent = Some(parent);
|
||||
self
|
||||
}
|
||||
|
||||
fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self {
|
||||
self.inh_kind = inh_kind;
|
||||
self
|
||||
}
|
||||
|
||||
fn build(self) -> ty::Generics {
|
||||
let mut own_params = vec![];
|
||||
|
||||
let sig_generics = self.tcx.generics_of(self.sig_id);
|
||||
if let InheritanceKind::WithParent(has_self) = self.inh_kind
|
||||
&& let Some(parent_def_id) = sig_generics.parent
|
||||
{
|
||||
let sig_parent_generics = self.tcx.generics_of(parent_def_id);
|
||||
own_params.append(&mut sig_parent_generics.own_params.clone());
|
||||
if !has_self {
|
||||
own_params.remove(0);
|
||||
}
|
||||
}
|
||||
own_params.append(&mut sig_generics.own_params.clone());
|
||||
|
||||
// Lifetime parameters must be declared before type and const parameters.
|
||||
// Therefore, When delegating from a free function to a associated function,
|
||||
// generic parameters need to be reordered:
|
||||
//
|
||||
// trait Trait<'a, A> {
|
||||
// fn foo<'b, B>(...) {...}
|
||||
// }
|
||||
//
|
||||
// reuse Trait::foo;
|
||||
// desugaring:
|
||||
// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
|
||||
// Trait::foo(...)
|
||||
// }
|
||||
own_params.sort_by_key(|key| key.kind.is_ty_or_const());
|
||||
|
||||
let param_def_id_to_index =
|
||||
own_params.iter().map(|param| (param.def_id, param.index)).collect();
|
||||
|
||||
let (parent_count, has_self) = if let Some(def_id) = self.parent {
|
||||
let parent_generics = self.tcx.generics_of(def_id);
|
||||
let parent_kind = self.tcx.def_kind(def_id);
|
||||
(parent_generics.count(), parent_kind == DefKind::Trait)
|
||||
} else {
|
||||
(0, false)
|
||||
};
|
||||
|
||||
for (idx, param) in own_params.iter_mut().enumerate() {
|
||||
param.index = (idx + parent_count) as u32;
|
||||
// FIXME(fn_delegation): Default parameters are not inherited, because they are
|
||||
// not permitted in functions. Therefore, there are 2 options here:
|
||||
//
|
||||
// - We can create non-default generic parameters.
|
||||
// - We can substitute default parameters into the signature.
|
||||
//
|
||||
// At the moment, first option has been selected as the most general.
|
||||
if let ty::GenericParamDefKind::Type { has_default, .. }
|
||||
| ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind
|
||||
{
|
||||
*has_default = false;
|
||||
}
|
||||
}
|
||||
|
||||
ty::Generics {
|
||||
parent: self.parent,
|
||||
parent_count,
|
||||
own_params,
|
||||
param_def_id_to_index,
|
||||
has_self,
|
||||
has_late_bound_regions: sig_generics.has_late_bound_regions,
|
||||
host_effect_index: sig_generics.host_effect_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PredicatesBuilder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sig_id: DefId,
|
||||
parent: Option<DefId>,
|
||||
inh_kind: InheritanceKind,
|
||||
args: ty::GenericArgsRef<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> PredicatesBuilder<'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: ty::GenericArgsRef<'tcx>,
|
||||
sig_id: DefId,
|
||||
) -> PredicatesBuilder<'tcx> {
|
||||
PredicatesBuilder {
|
||||
tcx,
|
||||
sig_id,
|
||||
parent: None,
|
||||
inh_kind: InheritanceKind::WithParent(false),
|
||||
args,
|
||||
}
|
||||
}
|
||||
|
||||
fn with_parent(mut self, parent: DefId) -> Self {
|
||||
self.parent = Some(parent);
|
||||
self
|
||||
}
|
||||
|
||||
fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self {
|
||||
self.inh_kind = inh_kind;
|
||||
self
|
||||
}
|
||||
|
||||
fn build(self) -> ty::GenericPredicates<'tcx> {
|
||||
struct PredicatesCollector<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
preds: Vec<(ty::Clause<'tcx>, Span)>,
|
||||
args: ty::GenericArgsRef<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> PredicatesCollector<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> PredicatesCollector<'tcx> {
|
||||
PredicatesCollector { tcx, preds: vec![], args }
|
||||
}
|
||||
|
||||
fn with_own_preds(
|
||||
mut self,
|
||||
f: impl Fn(DefId) -> ty::GenericPredicates<'tcx>,
|
||||
def_id: DefId,
|
||||
) -> Self {
|
||||
let preds = f(def_id).instantiate_own(self.tcx, self.args);
|
||||
self.preds.extend(preds);
|
||||
self
|
||||
}
|
||||
|
||||
fn with_preds(
|
||||
mut self,
|
||||
f: impl Fn(DefId) -> ty::GenericPredicates<'tcx> + Copy,
|
||||
def_id: DefId,
|
||||
) -> Self {
|
||||
let preds = f(def_id);
|
||||
if let Some(parent_def_id) = preds.parent {
|
||||
self = self.with_own_preds(f, parent_def_id);
|
||||
}
|
||||
self.with_own_preds(f, def_id)
|
||||
}
|
||||
}
|
||||
let collector = PredicatesCollector::new(self.tcx, self.args);
|
||||
|
||||
// `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate.
|
||||
// Note: `predicates_of` query can also add inferred outlives predicates, but that
|
||||
// is not the case here as `sig_id` is either a trait or a function.
|
||||
let preds = match self.inh_kind {
|
||||
InheritanceKind::WithParent(false) => {
|
||||
collector.with_preds(|def_id| self.tcx.explicit_predicates_of(def_id), self.sig_id)
|
||||
}
|
||||
InheritanceKind::WithParent(true) => {
|
||||
collector.with_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id)
|
||||
}
|
||||
InheritanceKind::Own => {
|
||||
collector.with_own_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id)
|
||||
}
|
||||
}
|
||||
.preds;
|
||||
|
||||
ty::GenericPredicates {
|
||||
parent: self.parent,
|
||||
predicates: self.tcx.arena.alloc_from_iter(preds),
|
||||
// FIXME(fn_delegation): Support effects.
|
||||
effects_min_tys: ty::List::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GenericArgsBuilder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
remap_table: RemapTable,
|
||||
sig_id: DefId,
|
||||
def_id: LocalDefId,
|
||||
}
|
||||
|
||||
impl<'tcx> GenericArgsBuilder<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, sig_id: DefId, def_id: LocalDefId) -> GenericArgsBuilder<'tcx> {
|
||||
GenericArgsBuilder { tcx, remap_table: FxHashMap::default(), sig_id, def_id }
|
||||
}
|
||||
|
||||
fn build_from_args(mut self, args: ty::GenericArgsRef<'tcx>) -> ty::GenericArgsRef<'tcx> {
|
||||
let caller_generics = self.tcx.generics_of(self.def_id);
|
||||
let callee_generics = self.tcx.generics_of(self.sig_id);
|
||||
|
||||
for caller_param in &caller_generics.own_params {
|
||||
let callee_index =
|
||||
callee_generics.param_def_id_to_index(self.tcx, caller_param.def_id).unwrap();
|
||||
self.remap_table.insert(callee_index, caller_param.index);
|
||||
}
|
||||
|
||||
let mut folder = ParamIndexRemapper { tcx: self.tcx, remap_table: self.remap_table };
|
||||
args.fold_with(&mut folder)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_generic_args<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
sig_id: DefId,
|
||||
) -> ty::GenericArgsRef<'tcx> {
|
||||
let caller_generics = tcx.generics_of(def_id);
|
||||
let callee_generics = tcx.generics_of(sig_id);
|
||||
let builder = GenericArgsBuilder::new(tcx, sig_id, def_id);
|
||||
|
||||
let caller_kind = fn_kind(tcx, def_id.into());
|
||||
let callee_kind = fn_kind(tcx, sig_id);
|
||||
// FIXME(fn_delegation): Support generics on associated delegation items.
|
||||
// Error will be reported in `check_constraints`.
|
||||
match (caller_kind, callee_kind) {
|
||||
(FnKind::Free, _) => {
|
||||
// Lifetime parameters must be declared before type and const parameters.
|
||||
// Therefore, When delegating from a free function to a associated function,
|
||||
// generic parameters need to be reordered:
|
||||
//
|
||||
// trait Trait<'a, A> {
|
||||
// fn foo<'b, B>(...) {...}
|
||||
// }
|
||||
//
|
||||
// reuse Trait::foo;
|
||||
// desugaring:
|
||||
// fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
|
||||
// Trait::foo(...)
|
||||
// }
|
||||
let mut remap_table = RemapTable::default();
|
||||
for caller_param in &caller_generics.own_params {
|
||||
let callee_index =
|
||||
callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap();
|
||||
remap_table.insert(callee_index, caller_param.index);
|
||||
}
|
||||
let mut folder = ParamIndexRemapper { tcx, remap_table };
|
||||
ty::GenericArgs::identity_for_item(tcx, sig_id).fold_with(&mut folder)
|
||||
(FnKind::Free, FnKind::Free)
|
||||
| (FnKind::Free, FnKind::AssocTrait)
|
||||
| (FnKind::AssocInherentImpl, FnKind::Free)
|
||||
| (FnKind::AssocTrait, FnKind::Free)
|
||||
| (FnKind::AssocTrait, FnKind::AssocTrait) => {
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, sig_id);
|
||||
builder.build_from_args(args)
|
||||
}
|
||||
// FIXME(fn_delegation): Only `Self` param supported here.
|
||||
(FnKind::AssocTraitImpl, FnKind::AssocTrait)
|
||||
| (FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
|
||||
|
||||
(FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
|
||||
let callee_generics = tcx.generics_of(sig_id);
|
||||
let parent = tcx.parent(def_id.into());
|
||||
let parent_args =
|
||||
tcx.impl_trait_header(parent).unwrap().trait_ref.instantiate_identity().args;
|
||||
|
||||
let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
|
||||
let method_args = tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count));
|
||||
let method_args = builder.build_from_args(method_args);
|
||||
|
||||
tcx.mk_args_from_iter(parent_args.iter().chain(method_args))
|
||||
}
|
||||
|
||||
(FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
|
||||
let parent = tcx.parent(def_id.into());
|
||||
let self_ty = tcx.type_of(parent).instantiate_identity();
|
||||
let generic_self_ty = ty::GenericArg::from(self_ty);
|
||||
tcx.mk_args_from_iter(std::iter::once(generic_self_ty))
|
||||
|
||||
let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
|
||||
let trait_args = builder.build_from_args(trait_args);
|
||||
|
||||
let args = std::iter::once(generic_self_ty).chain(trait_args.iter().skip(1));
|
||||
tcx.mk_args_from_iter(args)
|
||||
}
|
||||
_ => ty::GenericArgs::identity_for_item(tcx, sig_id),
|
||||
|
||||
// For trait impl's `sig_id` is always equal to the corresponding trait method.
|
||||
(FnKind::AssocTraitImpl, _)
|
||||
| (_, FnKind::AssocTraitImpl)
|
||||
// Delegation to inherent methods is not yet supported.
|
||||
| (_, FnKind::AssocInherentImpl) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): Move generics inheritance to the AST->HIR lowering.
|
||||
// For now, generic parameters are not propagated to the generated call,
|
||||
// which leads to inference errors:
|
||||
//
|
||||
// fn foo<T>(x: i32) {}
|
||||
//
|
||||
// reuse foo as bar;
|
||||
// desugaring:
|
||||
// fn bar<T>() {
|
||||
// foo::<_>() // ERROR: type annotations needed
|
||||
// }
|
||||
pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
sig_id: DefId,
|
||||
) -> Option<ty::Generics> {
|
||||
// FIXME(fn_delegation): Support generics on associated delegation items.
|
||||
// Error will be reported in `check_constraints`.
|
||||
if fn_kind(tcx, def_id.into()) != FnKind::Free {
|
||||
return None;
|
||||
}
|
||||
) -> ty::Generics {
|
||||
let builder = GenericsBuilder::new(tcx, sig_id);
|
||||
|
||||
let mut own_params = vec![];
|
||||
let caller_kind = fn_kind(tcx, def_id.into());
|
||||
let callee_kind = fn_kind(tcx, sig_id);
|
||||
match (caller_kind, callee_kind) {
|
||||
(FnKind::Free, FnKind::Free)
|
||||
| (FnKind::Free, FnKind::AssocTrait) => builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build(),
|
||||
|
||||
let callee_generics = tcx.generics_of(sig_id);
|
||||
if let Some(parent_sig_id) = callee_generics.parent {
|
||||
let parent_sig_generics = tcx.generics_of(parent_sig_id);
|
||||
own_params.append(&mut parent_sig_generics.own_params.clone());
|
||||
}
|
||||
own_params.append(&mut callee_generics.own_params.clone());
|
||||
|
||||
// Lifetimes go first.
|
||||
own_params.sort_by_key(|key| key.kind.is_ty_or_const());
|
||||
|
||||
for (idx, param) in own_params.iter_mut().enumerate() {
|
||||
param.index = idx as u32;
|
||||
// Default parameters are not inherited: they are not allowed
|
||||
// in fn's.
|
||||
if let ty::GenericParamDefKind::Type { has_default, .. }
|
||||
| ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind
|
||||
{
|
||||
*has_default = false;
|
||||
(FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
|
||||
builder
|
||||
.with_parent(tcx.parent(def_id.into()))
|
||||
.with_inheritance_kind(InheritanceKind::Own)
|
||||
.build()
|
||||
}
|
||||
|
||||
(FnKind::AssocInherentImpl, FnKind::AssocTrait)
|
||||
| (FnKind::AssocTrait, FnKind::AssocTrait) => {
|
||||
builder
|
||||
.with_parent(tcx.parent(def_id.into()))
|
||||
.build()
|
||||
}
|
||||
|
||||
(FnKind::AssocInherentImpl, FnKind::Free)
|
||||
| (FnKind::AssocTrait, FnKind::Free) => {
|
||||
builder
|
||||
.with_parent(tcx.parent(def_id.into()))
|
||||
.build()
|
||||
}
|
||||
|
||||
// For trait impl's `sig_id` is always equal to the corresponding trait method.
|
||||
(FnKind::AssocTraitImpl, _)
|
||||
| (_, FnKind::AssocTraitImpl)
|
||||
// Delegation to inherent methods is not yet supported.
|
||||
| (_, FnKind::AssocInherentImpl) => unreachable!(),
|
||||
}
|
||||
|
||||
let param_def_id_to_index =
|
||||
own_params.iter().map(|param| (param.def_id, param.index)).collect();
|
||||
|
||||
Some(ty::Generics {
|
||||
parent: None,
|
||||
parent_count: 0,
|
||||
own_params,
|
||||
param_def_id_to_index,
|
||||
has_self: false,
|
||||
has_late_bound_regions: callee_generics.has_late_bound_regions,
|
||||
host_effect_index: callee_generics.host_effect_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
sig_id: DefId,
|
||||
) -> Option<ty::GenericPredicates<'tcx>> {
|
||||
// FIXME(fn_delegation): Support generics on associated delegation items.
|
||||
// Error will be reported in `check_constraints`.
|
||||
if fn_kind(tcx, def_id.into()) != FnKind::Free {
|
||||
return None;
|
||||
}
|
||||
|
||||
let callee_predicates = tcx.predicates_of(sig_id);
|
||||
) -> ty::GenericPredicates<'tcx> {
|
||||
let args = create_generic_args(tcx, def_id, sig_id);
|
||||
let builder = PredicatesBuilder::new(tcx, args, sig_id);
|
||||
|
||||
let mut preds = vec![];
|
||||
if let Some(parent_id) = callee_predicates.parent {
|
||||
preds.extend(tcx.predicates_of(parent_id).instantiate_own(tcx, args));
|
||||
let caller_kind = fn_kind(tcx, def_id.into());
|
||||
let callee_kind = fn_kind(tcx, sig_id);
|
||||
match (caller_kind, callee_kind) {
|
||||
(FnKind::Free, FnKind::Free)
|
||||
| (FnKind::Free, FnKind::AssocTrait) => {
|
||||
builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build()
|
||||
}
|
||||
|
||||
(FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
|
||||
builder
|
||||
.with_parent(tcx.parent(def_id.into()))
|
||||
.with_inheritance_kind(InheritanceKind::Own)
|
||||
.build()
|
||||
}
|
||||
|
||||
(FnKind::AssocInherentImpl, FnKind::AssocTrait)
|
||||
| (FnKind::AssocTrait, FnKind::AssocTrait)
|
||||
| (FnKind::AssocInherentImpl, FnKind::Free)
|
||||
| (FnKind::AssocTrait, FnKind::Free) => {
|
||||
builder
|
||||
.with_parent(tcx.parent(def_id.into()))
|
||||
.build()
|
||||
}
|
||||
|
||||
// For trait impl's `sig_id` is always equal to the corresponding trait method.
|
||||
(FnKind::AssocTraitImpl, _)
|
||||
| (_, FnKind::AssocTraitImpl)
|
||||
// Delegation to inherent methods is not yet supported.
|
||||
| (_, FnKind::AssocInherentImpl) => unreachable!(),
|
||||
}
|
||||
preds.extend(callee_predicates.instantiate_own(tcx, args));
|
||||
|
||||
Some(ty::GenericPredicates {
|
||||
parent: None,
|
||||
predicates: tcx.arena.alloc_from_iter(preds),
|
||||
effects_min_tys: ty::List::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn check_constraints<'tcx>(
|
||||
|
|
@ -224,19 +482,6 @@ fn check_constraints<'tcx>(
|
|||
emit("recursive delegation is not supported yet");
|
||||
}
|
||||
|
||||
if fn_kind(tcx, def_id.into()) != FnKind::Free {
|
||||
let sig_generics = tcx.generics_of(sig_id);
|
||||
let parent = tcx.parent(def_id.into());
|
||||
let parent_generics = tcx.generics_of(parent);
|
||||
|
||||
let parent_has_self = parent_generics.has_self as usize;
|
||||
let sig_has_self = sig_generics.has_self as usize;
|
||||
|
||||
if sig_generics.count() > sig_has_self || parent_generics.count() > parent_has_self {
|
||||
emit("early bound generics are not supported for associated delegation items");
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,8 +72,11 @@ fn is_valid_cmse_inputs<'tcx>(
|
|||
let mut span = None;
|
||||
let mut accum = 0u64;
|
||||
|
||||
for (index, arg_def) in fn_sig.inputs().iter().enumerate() {
|
||||
let layout = tcx.layout_of(ParamEnv::reveal_all().and(*arg_def.skip_binder()))?;
|
||||
// this type is only used for layout computation, which does not rely on regions
|
||||
let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
|
||||
|
||||
for (index, ty) in fn_sig.inputs().iter().enumerate() {
|
||||
let layout = tcx.layout_of(ParamEnv::reveal_all().and(*ty))?;
|
||||
|
||||
let align = layout.layout.align().abi.bytes();
|
||||
let size = layout.layout.size().bytes();
|
||||
|
|
@ -98,7 +101,10 @@ fn is_valid_cmse_output<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
fn_sig: ty::PolyFnSig<'tcx>,
|
||||
) -> Result<bool, &'tcx LayoutError<'tcx>> {
|
||||
let mut ret_ty = fn_sig.output().skip_binder();
|
||||
// this type is only used for layout computation, which does not rely on regions
|
||||
let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
|
||||
|
||||
let mut ret_ty = fn_sig.output();
|
||||
let layout = tcx.layout_of(ParamEnv::reveal_all().and(ret_ty))?;
|
||||
let size = layout.layout.size().bytes();
|
||||
|
||||
|
|
|
|||
|
|
@ -447,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// this time with enough precision to check that the value
|
||||
// whose address was taken can actually be made to live as long
|
||||
// as it needs to live.
|
||||
let region = self.next_region_var(infer::AddrOfRegion(expr.span));
|
||||
let region = self.next_region_var(infer::BorrowRegion(expr.span));
|
||||
Ty::new_ref(self.tcx, region, ty, mutbl)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,8 +63,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Instead, the problem is that the array-into_iter hack will no longer
|
||||
// apply in Rust 2021.
|
||||
(ARRAY_INTO_ITER, "2021")
|
||||
} else if self_ty.is_box()
|
||||
&& self_ty.boxed_ty().is_slice()
|
||||
} else if self_ty.boxed_ty().is_some_and(Ty::is_slice)
|
||||
&& !span.at_least_rust_2024()
|
||||
{
|
||||
// In this case, it wasn't really a prelude addition that was the problem.
|
||||
|
|
|
|||
|
|
@ -715,7 +715,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) {
|
||||
let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey) else {
|
||||
let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::InstantiateWithInfer) else {
|
||||
bug!("unexpected incoherent type: {:?}", self_ty)
|
||||
};
|
||||
for &impl_def_id in self.tcx.incoherent_impls(simp).into_iter().flatten() {
|
||||
|
|
@ -1485,8 +1485,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
|
||||
// Some trait methods are excluded for boxed slices before 2024.
|
||||
// (`boxed_slice.into_iter()` wants a slice iterator for compatibility.)
|
||||
if self_ty.is_box()
|
||||
&& self_ty.boxed_ty().is_slice()
|
||||
if self_ty.boxed_ty().is_some_and(Ty::is_slice)
|
||||
&& !method_name.span.at_least_rust_2024()
|
||||
{
|
||||
let trait_def = self.tcx.trait_def(poly_trait_ref.def_id());
|
||||
|
|
|
|||
|
|
@ -2235,8 +2235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let target_ty = self
|
||||
.autoderef(sugg_span, rcvr_ty)
|
||||
.find(|(rcvr_ty, _)| {
|
||||
DeepRejectCtxt::new(self.tcx, TreatParams::ForLookup)
|
||||
.types_may_unify(*rcvr_ty, impl_ty)
|
||||
DeepRejectCtxt::relate_rigid_infer(self.tcx).types_may_unify(*rcvr_ty, impl_ty)
|
||||
})
|
||||
.map_or(impl_ty, |(ty, _)| ty)
|
||||
.peel_refs();
|
||||
|
|
@ -2498,7 +2497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.into_iter()
|
||||
.any(|info| self.associated_value(info.def_id, item_name).is_some());
|
||||
let found_assoc = |ty: Ty<'tcx>| {
|
||||
simplify_type(tcx, ty, TreatParams::AsCandidateKey)
|
||||
simplify_type(tcx, ty, TreatParams::InstantiateWithInfer)
|
||||
.and_then(|simp| {
|
||||
tcx.incoherent_impls(simp)
|
||||
.into_iter()
|
||||
|
|
@ -3963,7 +3962,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// cases where a positive bound implies a negative impl.
|
||||
(candidates, Vec::new())
|
||||
} else if let Some(simp_rcvr_ty) =
|
||||
simplify_type(self.tcx, rcvr_ty, TreatParams::ForLookup)
|
||||
simplify_type(self.tcx, rcvr_ty, TreatParams::AsRigid)
|
||||
{
|
||||
let mut potential_candidates = Vec::new();
|
||||
let mut explicitly_negative = Vec::new();
|
||||
|
|
@ -3981,7 +3980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.any(|header| {
|
||||
let imp = header.trait_ref.instantiate_identity();
|
||||
let imp_simp =
|
||||
simplify_type(self.tcx, imp.self_ty(), TreatParams::ForLookup);
|
||||
simplify_type(self.tcx, imp.self_ty(), TreatParams::AsRigid);
|
||||
imp_simp.is_some_and(|s| s == simp_rcvr_ty)
|
||||
})
|
||||
{
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_infer::infer::UpvarRegion;
|
||||
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind};
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
|
|
@ -425,7 +424,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.tcx,
|
||||
upvar_ty,
|
||||
capture,
|
||||
if needs_ref { Some(closure_env_region) } else { child_capture.region },
|
||||
if needs_ref {
|
||||
closure_env_region
|
||||
} else {
|
||||
self.tcx.lifetimes.re_erased
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -587,7 +590,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
debug!(?captured_place.place, ?upvar_ty, ?capture, ?captured_place.mutability);
|
||||
|
||||
apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region)
|
||||
apply_capture_kind_on_capture_ty(
|
||||
self.tcx,
|
||||
upvar_ty,
|
||||
capture,
|
||||
self.tcx.lifetimes.re_erased,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
@ -775,13 +783,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else {
|
||||
let mutability = self.determine_capture_mutability(&typeck_results, &place);
|
||||
let min_cap_list = vec![ty::CapturedPlace {
|
||||
var_ident,
|
||||
place,
|
||||
info: capture_info,
|
||||
mutability,
|
||||
region: None,
|
||||
}];
|
||||
let min_cap_list =
|
||||
vec![ty::CapturedPlace { var_ident, place, info: capture_info, mutability }];
|
||||
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
|
||||
continue;
|
||||
};
|
||||
|
|
@ -874,34 +877,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Only need to insert when we don't have an ancestor in the existing min capture list
|
||||
if !ancestor_found {
|
||||
let mutability = self.determine_capture_mutability(&typeck_results, &place);
|
||||
let captured_place = ty::CapturedPlace {
|
||||
var_ident,
|
||||
place,
|
||||
info: updated_capture_info,
|
||||
mutability,
|
||||
region: None,
|
||||
};
|
||||
let captured_place =
|
||||
ty::CapturedPlace { var_ident, place, info: updated_capture_info, mutability };
|
||||
min_cap_list.push(captured_place);
|
||||
}
|
||||
}
|
||||
|
||||
// For each capture that is determined to be captured by ref, add region info.
|
||||
for (_, captures) in &mut root_var_min_capture_list {
|
||||
for capture in captures {
|
||||
match capture.info.capture_kind {
|
||||
ty::UpvarCapture::ByRef(_) => {
|
||||
let PlaceBase::Upvar(upvar_id) = capture.place.base else {
|
||||
bug!("expected upvar")
|
||||
};
|
||||
let origin = UpvarRegion(upvar_id, closure_span);
|
||||
let upvar_region = self.next_region_var(origin);
|
||||
capture.region = Some(upvar_region);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"For closure={:?}, min_captures before sorting={:?}",
|
||||
closure_def_id, root_var_min_capture_list
|
||||
|
|
@ -1195,7 +1176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.tcx,
|
||||
ty,
|
||||
max_capture_info.capture_kind,
|
||||
Some(self.tcx.lifetimes.re_erased),
|
||||
self.tcx.lifetimes.re_erased,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
|
@ -1217,7 +1198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.tcx,
|
||||
capture.place.ty(),
|
||||
capture.info.capture_kind,
|
||||
Some(self.tcx.lifetimes.re_erased),
|
||||
self.tcx.lifetimes.re_erased,
|
||||
);
|
||||
|
||||
// Checks if a capture implements any of the auto traits
|
||||
|
|
@ -1935,13 +1916,11 @@ fn apply_capture_kind_on_capture_ty<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
capture_kind: UpvarCapture,
|
||||
region: Option<ty::Region<'tcx>>,
|
||||
region: ty::Region<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
match capture_kind {
|
||||
ty::UpvarCapture::ByValue => ty,
|
||||
ty::UpvarCapture::ByRef(kind) => {
|
||||
Ty::new_ref(tcx, region.unwrap(), ty, kind.to_mutbl_lossy())
|
||||
}
|
||||
ty::UpvarCapture::ByRef(kind) => Ty::new_ref(tcx, region, ty, kind.to_mutbl_lossy()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ mod tests;
|
|||
/// first value of the following element.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IntervalSet<I> {
|
||||
// Start, end
|
||||
map: SmallVec<[(u32, u32); 4]>,
|
||||
// Start, end (both inclusive)
|
||||
map: SmallVec<[(u32, u32); 2]>,
|
||||
domain: usize,
|
||||
_data: PhantomData<I>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -458,8 +458,8 @@ pub enum RegionVariableOrigin {
|
|||
PatternRegion(Span),
|
||||
|
||||
/// Regions created by `&` operator.
|
||||
///
|
||||
AddrOfRegion(Span),
|
||||
BorrowRegion(Span),
|
||||
|
||||
/// Regions created as part of an autoref of a method receiver.
|
||||
Autoref(Span),
|
||||
|
||||
|
|
@ -1741,7 +1741,7 @@ impl RegionVariableOrigin {
|
|||
match *self {
|
||||
MiscVariable(a)
|
||||
| PatternRegion(a)
|
||||
| AddrOfRegion(a)
|
||||
| BorrowRegion(a)
|
||||
| Autoref(a)
|
||||
| Coercion(a)
|
||||
| RegionParameterDefinition(a, ..)
|
||||
|
|
|
|||
|
|
@ -808,7 +808,7 @@ fn test_unstable_options_tracking_hash() {
|
|||
tracked!(mir_opt_level, Some(4));
|
||||
tracked!(move_size_limit, Some(4096));
|
||||
tracked!(mutable_noalias, false);
|
||||
tracked!(next_solver, Some(NextSolverConfig { coherence: true, globally: false }));
|
||||
tracked!(next_solver, NextSolverConfig { coherence: true, globally: true });
|
||||
tracked!(no_generate_arange_section, true);
|
||||
tracked!(no_jump_tables, true);
|
||||
tracked!(no_link, true);
|
||||
|
|
|
|||
|
|
@ -91,6 +91,15 @@ pub enum TokenKind {
|
|||
/// tokens.
|
||||
UnknownPrefix,
|
||||
|
||||
/// An unknown prefix in a lifetime, like `'foo#`.
|
||||
///
|
||||
/// Note that like above, only the `'` and prefix are included in the token
|
||||
/// and not the separator.
|
||||
UnknownPrefixLifetime,
|
||||
|
||||
/// `'r#lt`, which in edition < 2021 is split into several tokens: `'r # lt`.
|
||||
RawLifetime,
|
||||
|
||||
/// Similar to the above, but *always* an error on every edition. This is used
|
||||
/// for emoji identifier recovery, as those are not meant to be ever accepted.
|
||||
InvalidPrefix,
|
||||
|
|
@ -677,9 +686,17 @@ impl Cursor<'_> {
|
|||
return Literal { kind, suffix_start };
|
||||
}
|
||||
|
||||
if self.first() == 'r' && self.second() == '#' && is_id_start(self.third()) {
|
||||
// Eat "r" and `#`, and identifier start characters.
|
||||
self.bump();
|
||||
self.bump();
|
||||
self.bump();
|
||||
self.eat_while(is_id_continue);
|
||||
return RawLifetime;
|
||||
}
|
||||
|
||||
// Either a lifetime or a character literal with
|
||||
// length greater than 1.
|
||||
|
||||
let starts_with_number = self.first().is_ascii_digit();
|
||||
|
||||
// Skip the literal contents.
|
||||
|
|
@ -688,15 +705,17 @@ impl Cursor<'_> {
|
|||
self.bump();
|
||||
self.eat_while(is_id_continue);
|
||||
|
||||
// Check if after skipping literal contents we've met a closing
|
||||
// single quote (which means that user attempted to create a
|
||||
// string with single quotes).
|
||||
if self.first() == '\'' {
|
||||
self.bump();
|
||||
let kind = Char { terminated: true };
|
||||
Literal { kind, suffix_start: self.pos_within_token() }
|
||||
} else {
|
||||
Lifetime { starts_with_number }
|
||||
match self.first() {
|
||||
// Check if after skipping literal contents we've met a closing
|
||||
// single quote (which means that user attempted to create a
|
||||
// string with single quotes).
|
||||
'\'' => {
|
||||
self.bump();
|
||||
let kind = Char { terminated: true };
|
||||
Literal { kind, suffix_start: self.pos_within_token() }
|
||||
}
|
||||
'#' if !starts_with_number => UnknownPrefixLifetime,
|
||||
_ => Lifetime { starts_with_number },
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ lint_duplicate_matcher_binding = duplicate matcher binding
|
|||
lint_elided_named_lifetime = elided lifetime has a name
|
||||
.label_elided = this elided lifetime gets resolved as `{$name}`
|
||||
.label_named = lifetime `{$name}` declared here
|
||||
.suggestion = consider specifying it explicitly
|
||||
|
||||
lint_enum_intrinsics_mem_discriminant =
|
||||
the return value of `mem::discriminant` is unspecified when called with a non-enum type
|
||||
|
|
@ -699,11 +700,18 @@ lint_ptr_null_checks_ref = references are not nullable, so checking them for nul
|
|||
lint_query_instability = using `{$query}` can result in unstable query results
|
||||
.note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale
|
||||
|
||||
lint_query_untracked = `{$method}` accesses information that is not tracked by the query system
|
||||
.note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale
|
||||
|
||||
lint_range_endpoint_out_of_range = range endpoint is out of range for `{$ty}`
|
||||
|
||||
lint_range_use_inclusive_range = use an inclusive range instead
|
||||
|
||||
|
||||
lint_raw_prefix = prefix `'r` is reserved
|
||||
.label = reserved prefix
|
||||
.suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021
|
||||
|
||||
lint_reason_must_be_string_literal = reason must be a string literal
|
||||
|
||||
lint_reason_must_come_last = reason in lint attribute must come last
|
||||
|
|
|
|||
|
|
@ -1851,9 +1851,16 @@ impl KeywordIdents {
|
|||
TokenTree::Token(token, _) => {
|
||||
if let Some((ident, token::IdentIsRaw::No)) = token.ident() {
|
||||
if !prev_dollar {
|
||||
self.check_ident_token(cx, UnderMacro(true), ident);
|
||||
self.check_ident_token(cx, UnderMacro(true), ident, "");
|
||||
}
|
||||
} else if *token == TokenKind::Dollar {
|
||||
} else if let Some((ident, token::IdentIsRaw::No)) = token.lifetime() {
|
||||
self.check_ident_token(
|
||||
cx,
|
||||
UnderMacro(true),
|
||||
ident.without_first_quote(),
|
||||
"'",
|
||||
);
|
||||
} else if token.kind == TokenKind::Dollar {
|
||||
prev_dollar = true;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1869,6 +1876,7 @@ impl KeywordIdents {
|
|||
cx: &EarlyContext<'_>,
|
||||
UnderMacro(under_macro): UnderMacro,
|
||||
ident: Ident,
|
||||
prefix: &'static str,
|
||||
) {
|
||||
let (lint, edition) = match ident.name {
|
||||
kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018),
|
||||
|
|
@ -1902,7 +1910,7 @@ impl KeywordIdents {
|
|||
cx.emit_span_lint(
|
||||
lint,
|
||||
ident.span,
|
||||
BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span },
|
||||
BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span, prefix },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1915,7 +1923,11 @@ impl EarlyLintPass for KeywordIdents {
|
|||
self.check_tokens(cx, &mac.args.tokens);
|
||||
}
|
||||
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
|
||||
self.check_ident_token(cx, UnderMacro(false), ident);
|
||||
if ident.name.as_str().starts_with('\'') {
|
||||
self.check_ident_token(cx, UnderMacro(false), ident.without_first_quote(), "'");
|
||||
} else {
|
||||
self.check_ident_token(cx, UnderMacro(false), ident, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ use rustc_errors::{
|
|||
elided_lifetime_in_path_suggestion, Applicability, Diag, DiagArgValue, LintDiagnostic,
|
||||
};
|
||||
use rustc_middle::middle::stability;
|
||||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::{BuiltinLintDiag, ElidedLifetimeResolution};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::BytePos;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::lints;
|
||||
use crate::lints::{self, ElidedNamedLifetime};
|
||||
|
||||
mod check_cfg;
|
||||
|
||||
|
|
@ -172,6 +172,10 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
|
|||
}
|
||||
.decorate_lint(diag);
|
||||
}
|
||||
BuiltinLintDiag::RawPrefix(label_span) => {
|
||||
lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
|
||||
.decorate_lint(diag);
|
||||
}
|
||||
BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
|
||||
lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag);
|
||||
}
|
||||
|
|
@ -442,15 +446,14 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
|
|||
BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => {
|
||||
lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag)
|
||||
}
|
||||
BuiltinLintDiag::ElidedIsStatic { elided } => {
|
||||
lints::ElidedNamedLifetime { elided, name: kw::StaticLifetime, named_declaration: None }
|
||||
.decorate_lint(diag)
|
||||
}
|
||||
BuiltinLintDiag::ElidedIsParam { elided, param: (param_name, param_span) } => {
|
||||
lints::ElidedNamedLifetime {
|
||||
elided,
|
||||
name: param_name,
|
||||
named_declaration: Some(param_span),
|
||||
BuiltinLintDiag::ElidedNamedLifetimes { elided: (span, kind), resolution } => {
|
||||
match resolution {
|
||||
ElidedLifetimeResolution::Static => {
|
||||
ElidedNamedLifetime { span, kind, name: kw::StaticLifetime, declaration: None }
|
||||
}
|
||||
ElidedLifetimeResolution::Param(name, declaration) => {
|
||||
ElidedNamedLifetime { span, kind, name, declaration: Some(declaration) }
|
||||
}
|
||||
}
|
||||
.decorate_lint(diag)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ use tracing::debug;
|
|||
|
||||
use crate::lints::{
|
||||
BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword,
|
||||
NonGlobImportTypeIrInherent, QueryInstability, SpanUseEqCtxtDiag, TyQualified, TykindDiag,
|
||||
TykindKind, TypeIrInherentUsage, UntranslatableDiag,
|
||||
NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, TyQualified,
|
||||
TykindDiag, TykindKind, TypeIrInherentUsage, UntranslatableDiag,
|
||||
};
|
||||
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
|
||||
|
|
@ -88,7 +88,18 @@ declare_tool_lint! {
|
|||
report_in_external_macro: true
|
||||
}
|
||||
|
||||
declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY]);
|
||||
declare_tool_lint! {
|
||||
/// The `untracked_query_information` lint detects use of methods which leak information not
|
||||
/// tracked by the query system, such as whether a `Steal<T>` value has already been stolen. In
|
||||
/// order not to break incremental compilation, such methods must be used very carefully or not
|
||||
/// at all.
|
||||
pub rustc::UNTRACKED_QUERY_INFORMATION,
|
||||
Allow,
|
||||
"require explicit opt-in when accessing information not tracked by the query system",
|
||||
report_in_external_macro: true
|
||||
}
|
||||
|
||||
declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);
|
||||
|
||||
impl LateLintPass<'_> for QueryStability {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
|
|
@ -102,6 +113,13 @@ impl LateLintPass<'_> for QueryStability {
|
|||
QueryInstability { query: cx.tcx.item_name(def_id) },
|
||||
);
|
||||
}
|
||||
if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) {
|
||||
cx.emit_span_lint(
|
||||
UNTRACKED_QUERY_INFORMATION,
|
||||
span,
|
||||
QueryUntracked { method: cx.tcx.item_name(def_id) },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -576,6 +576,10 @@ fn register_builtins(store: &mut LintStore) {
|
|||
<https://github.com/rust-lang/rust/issues/107457> for more information",
|
||||
);
|
||||
store.register_removed("writes_through_immutable_pointer", "converted into hard error");
|
||||
store.register_removed(
|
||||
"const_eval_mutable_ptr_in_final_value",
|
||||
"partially allowed now, otherwise turned into a hard error",
|
||||
);
|
||||
}
|
||||
|
||||
fn register_internals(store: &mut LintStore) {
|
||||
|
|
@ -610,6 +614,7 @@ fn register_internals(store: &mut LintStore) {
|
|||
vec![
|
||||
LintId::of(DEFAULT_HASH_TYPES),
|
||||
LintId::of(POTENTIAL_QUERY_INSTABILITY),
|
||||
LintId::of(UNTRACKED_QUERY_INFORMATION),
|
||||
LintId::of(USAGE_OF_TY_TYKIND),
|
||||
LintId::of(PASS_BY_VALUE),
|
||||
LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use rustc_errors::{
|
|||
};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir};
|
||||
use rustc_hir::{self as hir, MissingLifetimeKind};
|
||||
use rustc_macros::{LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::inhabitedness::InhabitedPredicate;
|
||||
use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt};
|
||||
|
|
@ -363,8 +363,9 @@ pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint {
|
|||
pub(crate) struct BuiltinKeywordIdents {
|
||||
pub kw: Ident,
|
||||
pub next: Edition,
|
||||
#[suggestion(code = "r#{kw}", applicability = "machine-applicable")]
|
||||
#[suggestion(code = "{prefix}r#{kw}", applicability = "machine-applicable")]
|
||||
pub suggestion: Span,
|
||||
pub prefix: &'static str,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
|
@ -894,6 +895,13 @@ pub(crate) struct QueryInstability {
|
|||
pub query: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_query_untracked)]
|
||||
#[note]
|
||||
pub(crate) struct QueryUntracked {
|
||||
pub method: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_span_use_eq_ctxt)]
|
||||
pub(crate) struct SpanUseEqCtxtDiag;
|
||||
|
|
@ -2616,14 +2624,56 @@ pub(crate) struct ElidedLifetimesInPaths {
|
|||
pub subdiag: ElidedLifetimeInPathSubdiag,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_elided_named_lifetime)]
|
||||
pub(crate) struct ElidedNamedLifetime {
|
||||
#[label(lint_label_elided)]
|
||||
pub elided: Span,
|
||||
pub span: Span,
|
||||
pub kind: MissingLifetimeKind,
|
||||
pub name: Symbol,
|
||||
#[label(lint_label_named)]
|
||||
pub named_declaration: Option<Span>,
|
||||
pub declaration: Option<Span>,
|
||||
}
|
||||
|
||||
impl<G: EmissionGuarantee> LintDiagnostic<'_, G> for ElidedNamedLifetime {
|
||||
fn decorate_lint(self, diag: &mut rustc_errors::Diag<'_, G>) {
|
||||
let Self { span, kind, name, declaration } = self;
|
||||
diag.primary_message(fluent::lint_elided_named_lifetime);
|
||||
diag.arg("name", name);
|
||||
diag.span_label(span, fluent::lint_label_elided);
|
||||
if let Some(declaration) = declaration {
|
||||
diag.span_label(declaration, fluent::lint_label_named);
|
||||
}
|
||||
// FIXME(GrigorenkoPV): this `if` and `return` should be removed,
|
||||
// but currently this lint's suggestions can conflict with those of `clippy::needless_lifetimes`:
|
||||
// https://github.com/rust-lang/rust/pull/129840#issuecomment-2323349119
|
||||
// HACK: `'static` suggestions will never sonflict, emit only those for now.
|
||||
if name != rustc_span::symbol::kw::StaticLifetime {
|
||||
return;
|
||||
}
|
||||
match kind {
|
||||
MissingLifetimeKind::Underscore => diag.span_suggestion_verbose(
|
||||
span,
|
||||
fluent::lint_suggestion,
|
||||
format!("{name}"),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
MissingLifetimeKind::Ampersand => diag.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
fluent::lint_suggestion,
|
||||
format!("{name} "),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
MissingLifetimeKind::Comma => diag.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
fluent::lint_suggestion,
|
||||
format!("{name}, "),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
MissingLifetimeKind::Brackets => diag.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
fluent::lint_suggestion,
|
||||
format!("<{name}>"),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
|
@ -2765,6 +2815,15 @@ pub(crate) struct ReservedPrefix {
|
|||
pub prefix: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_raw_prefix)]
|
||||
pub(crate) struct RawPrefix {
|
||||
#[label]
|
||||
pub label: Span,
|
||||
#[suggestion(code = " ", applicability = "machine-applicable")]
|
||||
pub suggestion: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_unused_builtin_attribute)]
|
||||
pub(crate) struct UnusedBuiltinAttribute {
|
||||
|
|
|
|||
|
|
@ -94,12 +94,9 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
|
|||
fn is_ref_to_array(ty: Ty<'_>) -> bool {
|
||||
if let ty::Ref(_, pointee_ty, _) = *ty.kind() { pointee_ty.is_array() } else { false }
|
||||
}
|
||||
fn is_boxed_slice(ty: Ty<'_>) -> bool {
|
||||
ty.is_box() && ty.boxed_ty().is_slice()
|
||||
}
|
||||
fn is_ref_to_boxed_slice(ty: Ty<'_>) -> bool {
|
||||
if let ty::Ref(_, pointee_ty, _) = *ty.kind() {
|
||||
is_boxed_slice(pointee_ty)
|
||||
pointee_ty.boxed_ty().is_some_and(Ty::is_slice)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -119,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
|
|||
.iter()
|
||||
.copied()
|
||||
.take_while(|ty| !is_ref_to_boxed_slice(*ty))
|
||||
.position(|ty| is_boxed_slice(ty))
|
||||
.position(|ty| ty.boxed_ty().is_some_and(Ty::is_slice))
|
||||
{
|
||||
(BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1304,8 +1304,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
|
||||
match *ty.kind() {
|
||||
ty::Adt(def, args) => {
|
||||
if def.is_box() && matches!(self.mode, CItemKind::Definition) {
|
||||
if ty.boxed_ty().is_sized(tcx, self.cx.param_env) {
|
||||
if let Some(boxed) = ty.boxed_ty()
|
||||
&& matches!(self.mode, CItemKind::Definition)
|
||||
{
|
||||
if boxed.is_sized(tcx, self.cx.param_env) {
|
||||
return FfiSafe;
|
||||
} else {
|
||||
return FfiUnsafe {
|
||||
|
|
|
|||
|
|
@ -283,9 +283,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
|||
}
|
||||
|
||||
match *ty.kind() {
|
||||
ty::Adt(..) if ty.is_box() => {
|
||||
let boxed_ty = ty.boxed_ty();
|
||||
is_ty_must_use(cx, boxed_ty, expr, span)
|
||||
ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
|
||||
is_ty_must_use(cx, boxed, expr, span)
|
||||
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
|
||||
}
|
||||
ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ declare_lint_pass! {
|
|||
CENUM_IMPL_DROP_CAST,
|
||||
COHERENCE_LEAK_CHECK,
|
||||
CONFLICTING_REPR_HINTS,
|
||||
CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE,
|
||||
CONST_EVALUATABLE_UNCHECKED,
|
||||
CONST_ITEM_MUTATION,
|
||||
DEAD_CODE,
|
||||
|
|
@ -2804,51 +2803,6 @@ declare_lint! {
|
|||
@feature_gate = strict_provenance;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `const_eval_mutable_ptr_in_final_value` lint detects if a mutable pointer
|
||||
/// has leaked into the final value of a const expression.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// pub enum JsValue {
|
||||
/// Undefined,
|
||||
/// Object(std::cell::Cell<bool>),
|
||||
/// }
|
||||
///
|
||||
/// impl ::std::ops::Drop for JsValue {
|
||||
/// fn drop(&mut self) {}
|
||||
/// }
|
||||
///
|
||||
/// const UNDEFINED: &JsValue = &JsValue::Undefined;
|
||||
///
|
||||
/// fn main() {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// In the 1.77 release, the const evaluation machinery adopted some
|
||||
/// stricter rules to reject expressions with values that could
|
||||
/// end up holding mutable references to state stored in static memory
|
||||
/// (which is inherently immutable).
|
||||
///
|
||||
/// This is a [future-incompatible] lint to ease the transition to an error.
|
||||
/// See [issue #122153] for more details.
|
||||
///
|
||||
/// [issue #122153]: https://github.com/rust-lang/rust/issues/122153
|
||||
/// [future-incompatible]: ../index.md#future-incompatible-lints
|
||||
pub CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE,
|
||||
Warn,
|
||||
"detects a mutable pointer that has leaked into final value of a const expression",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
|
||||
reference: "issue #122153 <https://github.com/rust-lang/rust/issues/122153>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `const_evaluatable_unchecked` lint detects a generic constant used
|
||||
/// in a type.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_data_structures::stable_hasher::{
|
|||
};
|
||||
use rustc_error_messages::{DiagMessage, MultiSpan};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::{HashStableContext, HirId};
|
||||
use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent};
|
||||
|
|
@ -556,6 +556,12 @@ pub enum DeprecatedSinceKind {
|
|||
InVersion(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ElidedLifetimeResolution {
|
||||
Static,
|
||||
Param(Symbol, Span),
|
||||
}
|
||||
|
||||
// This could be a closure, but then implementing derive trait
|
||||
// becomes hacky (and it gets allocated).
|
||||
#[derive(Debug)]
|
||||
|
|
@ -568,12 +574,9 @@ pub enum BuiltinLintDiag {
|
|||
},
|
||||
MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
|
||||
ElidedLifetimesInPaths(usize, Span, bool, Span),
|
||||
ElidedIsStatic {
|
||||
elided: Span,
|
||||
},
|
||||
ElidedIsParam {
|
||||
elided: Span,
|
||||
param: (Symbol, Span),
|
||||
ElidedNamedLifetimes {
|
||||
elided: (Span, MissingLifetimeKind),
|
||||
resolution: ElidedLifetimeResolution,
|
||||
},
|
||||
UnknownCrateTypes {
|
||||
span: Span,
|
||||
|
|
@ -609,6 +612,8 @@ pub enum BuiltinLintDiag {
|
|||
LegacyDeriveHelpers(Span),
|
||||
OrPatternsBackCompat(Span, String),
|
||||
ReservedPrefix(Span, String),
|
||||
/// `'r#` in edition < 2021.
|
||||
RawPrefix(Span),
|
||||
TrailingMacro(bool, Ident),
|
||||
BreakWithLabelAndLoop(Span),
|
||||
UnicodeTextFlow(Span, String),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(bootstrap, feature(unsafe_attributes, unsafe_extern_blocks))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
|||
use rustc_data_structures::memmap::{Mmap, MmapMut};
|
||||
use rustc_data_structures::sync::{join, par_for_each_in, Lrc};
|
||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{LocalDefId, LocalDefIdSet, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
|
|
@ -797,9 +798,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct AnalyzeAttrState {
|
||||
struct AnalyzeAttrState<'a> {
|
||||
is_exported: bool,
|
||||
is_doc_hidden: bool,
|
||||
features: &'a Features,
|
||||
}
|
||||
|
||||
/// Returns whether an attribute needs to be recorded in metadata, that is, if it's usable and
|
||||
|
|
@ -812,7 +814,7 @@ struct AnalyzeAttrState {
|
|||
/// visibility: this is a piece of data that can be computed once per defid, and not once per
|
||||
/// attribute. Some attributes would only be usable downstream if they are public.
|
||||
#[inline]
|
||||
fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool {
|
||||
fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState<'_>) -> bool {
|
||||
let mut should_encode = false;
|
||||
if !rustc_feature::encode_cross_crate(attr.name_or_empty()) {
|
||||
// Attributes not marked encode-cross-crate don't need to be encoded for downstream crates.
|
||||
|
|
@ -837,6 +839,9 @@ fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if attr.path().starts_with(&[sym::diagnostic]) && attr.path().len() == 2 {
|
||||
should_encode =
|
||||
rustc_feature::is_stable_diagnostic_attribute(attr.path()[1], state.features);
|
||||
} else {
|
||||
should_encode = true;
|
||||
}
|
||||
|
|
@ -1343,6 +1348,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
let mut state = AnalyzeAttrState {
|
||||
is_exported: tcx.effective_visibilities(()).is_exported(def_id),
|
||||
is_doc_hidden: false,
|
||||
features: &tcx.features(),
|
||||
};
|
||||
let attr_iter = tcx
|
||||
.hir()
|
||||
|
|
@ -2033,7 +2039,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
let simplified_self_ty = fast_reject::simplify_type(
|
||||
self.tcx,
|
||||
trait_ref.self_ty(),
|
||||
TreatParams::AsCandidateKey,
|
||||
TreatParams::InstantiateWithInfer,
|
||||
);
|
||||
trait_impls
|
||||
.entry(trait_ref.def_id)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(bootstrap, feature(min_exhaustive_patterns, unsafe_extern_blocks))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(allocator_api)]
|
||||
|
|
|
|||
|
|
@ -644,6 +644,12 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
/// Remove all provenance in the given memory range.
|
||||
pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
|
||||
self.provenance.clear(range, cx)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
/// Applies a previously prepared provenance copy.
|
||||
/// The affected range, as defined in the parameters to `provenance().prepare_copy` is expected
|
||||
/// to be clear of provenance.
|
||||
|
|
|
|||
|
|
@ -80,14 +80,23 @@ pub trait Provenance: Copy + fmt::Debug + 'static {
|
|||
}
|
||||
|
||||
/// The type of provenance in the compile-time interpreter.
|
||||
/// This is a packed representation of an `AllocId` and an `immutable: bool`.
|
||||
/// This is a packed representation of:
|
||||
/// - an `AllocId` (non-zero)
|
||||
/// - an `immutable: bool`
|
||||
/// - a `shared_ref: bool`
|
||||
///
|
||||
/// with the extra invariant that if `immutable` is `true`, then so
|
||||
/// is `shared_ref`.
|
||||
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct CtfeProvenance(NonZero<u64>);
|
||||
|
||||
impl From<AllocId> for CtfeProvenance {
|
||||
fn from(value: AllocId) -> Self {
|
||||
let prov = CtfeProvenance(value.0);
|
||||
assert!(!prov.immutable(), "`AllocId` with the highest bit set cannot be used in CTFE");
|
||||
assert!(
|
||||
prov.alloc_id() == value,
|
||||
"`AllocId` with the highest bits set cannot be used in CTFE"
|
||||
);
|
||||
prov
|
||||
}
|
||||
}
|
||||
|
|
@ -103,12 +112,14 @@ impl fmt::Debug for CtfeProvenance {
|
|||
}
|
||||
|
||||
const IMMUTABLE_MASK: u64 = 1 << 63; // the highest bit
|
||||
const SHARED_REF_MASK: u64 = 1 << 62;
|
||||
const ALLOC_ID_MASK: u64 = u64::MAX & !IMMUTABLE_MASK & !SHARED_REF_MASK;
|
||||
|
||||
impl CtfeProvenance {
|
||||
/// Returns the `AllocId` of this provenance.
|
||||
#[inline(always)]
|
||||
pub fn alloc_id(self) -> AllocId {
|
||||
AllocId(NonZero::new(self.0.get() & !IMMUTABLE_MASK).unwrap())
|
||||
AllocId(NonZero::new(self.0.get() & ALLOC_ID_MASK).unwrap())
|
||||
}
|
||||
|
||||
/// Returns whether this provenance is immutable.
|
||||
|
|
@ -117,10 +128,38 @@ impl CtfeProvenance {
|
|||
self.0.get() & IMMUTABLE_MASK != 0
|
||||
}
|
||||
|
||||
/// Returns whether this provenance is derived from a shared reference.
|
||||
#[inline]
|
||||
pub fn shared_ref(self) -> bool {
|
||||
self.0.get() & SHARED_REF_MASK != 0
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (AllocId, bool, bool) {
|
||||
(self.alloc_id(), self.immutable(), self.shared_ref())
|
||||
}
|
||||
|
||||
pub fn from_parts((alloc_id, immutable, shared_ref): (AllocId, bool, bool)) -> Self {
|
||||
let prov = CtfeProvenance::from(alloc_id);
|
||||
if immutable {
|
||||
// This sets both flags, so we don't even have to check `shared_ref`.
|
||||
prov.as_immutable()
|
||||
} else if shared_ref {
|
||||
prov.as_shared_ref()
|
||||
} else {
|
||||
prov
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an immutable version of this provenance.
|
||||
#[inline]
|
||||
pub fn as_immutable(self) -> Self {
|
||||
CtfeProvenance(self.0 | IMMUTABLE_MASK)
|
||||
CtfeProvenance(self.0 | IMMUTABLE_MASK | SHARED_REF_MASK)
|
||||
}
|
||||
|
||||
/// Returns a "shared reference" (but not necessarily immutable!) version of this provenance.
|
||||
#[inline]
|
||||
pub fn as_shared_ref(self) -> Self {
|
||||
CtfeProvenance(self.0 | SHARED_REF_MASK)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -307,6 +307,13 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn clear_provenance(&mut self) -> InterpResult<'tcx> {
|
||||
if matches!(self, Scalar::Ptr(..)) {
|
||||
*self = self.to_scalar_int()?.into();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> {
|
||||
self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into())
|
||||
|
|
|
|||
|
|
@ -1307,6 +1307,9 @@ pub enum Rvalue<'tcx> {
|
|||
/// If the type of the place is an array, this is the array length. For slices (`[T]`, not
|
||||
/// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
|
||||
/// ill-formed for places of other types.
|
||||
///
|
||||
/// This cannot be a `UnOp(PtrMetadata, _)` because that expects a value, and we only
|
||||
/// have a place, and `UnOp(PtrMetadata, RawPtr(place))` is not a thing.
|
||||
Len(Place<'tcx>),
|
||||
|
||||
/// Performs essentially all of the casts that can be performed via `as`.
|
||||
|
|
|
|||
|
|
@ -88,9 +88,6 @@ pub struct CapturedPlace<'tcx> {
|
|||
|
||||
/// Represents if `place` can be mutated or not.
|
||||
pub mutability: hir::Mutability,
|
||||
|
||||
/// Region of the resulting reference if the upvar is captured by ref.
|
||||
pub region: Option<ty::Region<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> CapturedPlace<'tcx> {
|
||||
|
|
|
|||
|
|
@ -165,8 +165,7 @@ impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for AllocId {
|
|||
|
||||
impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for CtfeProvenance {
|
||||
fn encode(&self, e: &mut E) {
|
||||
self.alloc_id().encode(e);
|
||||
self.immutable().encode(e);
|
||||
self.into_parts().encode(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -295,10 +294,8 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for AllocId {
|
|||
|
||||
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for CtfeProvenance {
|
||||
fn decode(decoder: &mut D) -> Self {
|
||||
let alloc_id: AllocId = Decodable::decode(decoder);
|
||||
let prov = CtfeProvenance::from(alloc_id);
|
||||
let immutable: bool = Decodable::decode(decoder);
|
||||
if immutable { prov.as_immutable() } else { prov }
|
||||
let parts = Decodable::decode(decoder);
|
||||
CtfeProvenance::from_parts(parts)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
let simp = ty::fast_reject::simplify_type(
|
||||
tcx,
|
||||
self_ty,
|
||||
ty::fast_reject::TreatParams::ForLookup,
|
||||
ty::fast_reject::TreatParams::AsRigid,
|
||||
)
|
||||
.unwrap();
|
||||
consider_impls_for_simplified_type(simp);
|
||||
|
|
@ -3128,11 +3128,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}
|
||||
|
||||
pub fn next_trait_solver_globally(self) -> bool {
|
||||
self.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.globally)
|
||||
self.sess.opts.unstable_opts.next_solver.globally
|
||||
}
|
||||
|
||||
pub fn next_trait_solver_in_coherence(self) -> bool {
|
||||
self.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.coherence)
|
||||
self.sess.opts.unstable_opts.next_solver.coherence
|
||||
}
|
||||
|
||||
pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
|
||||
|
|
@ -3183,8 +3183,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
/// Whether this is a trait implementation that has `#[diagnostic::do_not_recommend]`
|
||||
pub fn do_not_recommend_impl(self, def_id: DefId) -> bool {
|
||||
matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true })
|
||||
&& self.impl_trait_header(def_id).is_some_and(|header| header.do_not_recommend)
|
||||
self.get_diagnostic_attr(def_id, sym::do_not_recommend).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@ pub use rustc_type_ir::fast_reject::*;
|
|||
|
||||
use super::TyCtxt;
|
||||
|
||||
pub type DeepRejectCtxt<'tcx> = rustc_type_ir::fast_reject::DeepRejectCtxt<TyCtxt<'tcx>>;
|
||||
pub type DeepRejectCtxt<
|
||||
'tcx,
|
||||
const INSTANTIATE_LHS_WITH_INFER: bool,
|
||||
const INSTANTIATE_RHS_WITH_INFER: bool,
|
||||
> = rustc_type_ir::fast_reject::DeepRejectCtxt<
|
||||
TyCtxt<'tcx>,
|
||||
INSTANTIATE_LHS_WITH_INFER,
|
||||
INSTANTIATE_RHS_WITH_INFER,
|
||||
>;
|
||||
|
||||
pub type SimplifiedType = rustc_type_ir::fast_reject::SimplifiedType<DefId>;
|
||||
|
|
|
|||
|
|
@ -75,11 +75,9 @@ impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
|
|||
}
|
||||
}
|
||||
|
||||
// CtfeProvenance is an AllocId and a bool.
|
||||
impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::CtfeProvenance {
|
||||
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
|
||||
self.alloc_id().hash_stable(hcx, hasher);
|
||||
self.immutable().hash_stable(hcx, hasher);
|
||||
self.into_parts().hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1075,11 +1075,13 @@ where
|
|||
// the raw pointer, so size and align are set to the boxed type, but `pointee.safe`
|
||||
// will still be `None`.
|
||||
if let Some(ref mut pointee) = result {
|
||||
if offset.bytes() == 0 && this.ty.is_box() {
|
||||
if offset.bytes() == 0
|
||||
&& let Some(boxed_ty) = this.ty.boxed_ty()
|
||||
{
|
||||
debug_assert!(pointee.safe.is_none());
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
pointee.safe = Some(PointerKind::Box {
|
||||
unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
|
||||
unpin: optimize && boxed_ty.is_unpin(tcx, cx.param_env()),
|
||||
global: this.ty.is_box_global(tcx),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,7 +263,6 @@ pub struct ImplTraitHeader<'tcx> {
|
|||
pub trait_ref: ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>>,
|
||||
pub polarity: ImplPolarity,
|
||||
pub safety: hir::Safety,
|
||||
pub do_not_recommend: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
|
||||
|
|
@ -1797,6 +1796,37 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get an attribute from the diagnostic attribute namespace
|
||||
///
|
||||
/// This function requests an attribute with the following structure:
|
||||
///
|
||||
/// `#[diagnostic::$attr]`
|
||||
///
|
||||
/// This function performs feature checking, so if an attribute is returned
|
||||
/// it can be used by the consumer
|
||||
pub fn get_diagnostic_attr(
|
||||
self,
|
||||
did: impl Into<DefId>,
|
||||
attr: Symbol,
|
||||
) -> Option<&'tcx ast::Attribute> {
|
||||
let did: DefId = did.into();
|
||||
if did.as_local().is_some() {
|
||||
// it's a crate local item, we need to check feature flags
|
||||
if rustc_feature::is_stable_diagnostic_attribute(attr, self.features()) {
|
||||
self.get_attrs_by_path(did, &[sym::diagnostic, sym::do_not_recommend]).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// we filter out unstable diagnostic attributes before
|
||||
// encoding attributes
|
||||
debug_assert!(rustc_feature::encode_cross_crate(attr));
|
||||
self.item_attrs(did)
|
||||
.iter()
|
||||
.find(|a| matches!(a.path().as_ref(), [sym::diagnostic, a] if *a == attr))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_attrs_by_path<'attr>(
|
||||
self,
|
||||
did: DefId,
|
||||
|
|
|
|||
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