mirror of
https://git.proxmox.com/git/rustc
synced 2026-01-26 04:25:31 +00:00
New upstream version 1.76.0+dfsg1
This commit is contained in:
parent
ed00b5ec96
commit
4b01247206
@ -15,7 +15,7 @@ standard library in the [Standard library developers Guide][std-dev-guide], comm
|
||||
## About the [rustc-dev-guide]
|
||||
|
||||
The [rustc-dev-guide] is meant to help document how rustc –the Rust compiler– works,
|
||||
as well as to help new contributors get involved in rustc development. It is recommend
|
||||
as well as to help new contributors get involved in rustc development. It is recommended
|
||||
to read and understand the [rustc-dev-guide] before making a contribution. This guide
|
||||
talks about the different bots in the Rust ecosystem, the Rust development tools,
|
||||
bootstrapping, the compiler architecture, source code representation, and more.
|
||||
|
||||
437
Cargo.lock
generated
437
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,7 @@ If you wish to _contribute_ to the compiler, you should read
|
||||
[CONTRIBUTING.md](CONTRIBUTING.md) instead.
|
||||
|
||||
<details>
|
||||
<summary>Table of content</summary>
|
||||
<summary>Table of Contents</summary>
|
||||
|
||||
- [Quick Start](#quick-start)
|
||||
- [Installing from Source](#installing-from-source)
|
||||
|
||||
93
RELEASES.md
93
RELEASES.md
@ -1,3 +1,96 @@
|
||||
Version 1.76.0 (2024-02-08)
|
||||
==========================
|
||||
|
||||
<a id="1.76.0-Language"></a>
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Document Rust ABI compatibility between various types](https://github.com/rust-lang/rust/pull/115476/)
|
||||
- [Also: guarantee that char and u32 are ABI-compatible](https://github.com/rust-lang/rust/pull/118032/)
|
||||
- [Warn against ambiguous wide pointer comparisons](https://github.com/rust-lang/rust/pull/117758/)
|
||||
|
||||
<a id="1.76.0-Compiler"></a>
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [Lint pinned `#[must_use]` pointers (in particular, `Box<T>` where `T` is `#[must_use]`) in `unused_must_use`.](https://github.com/rust-lang/rust/pull/118054/)
|
||||
- [Soundness fix: fix computing the offset of an unsized field in a packed struct](https://github.com/rust-lang/rust/pull/118540/)
|
||||
- [Soundness fix: fix dynamic size/align computation logic for packed types with dyn Trait tail](https://github.com/rust-lang/rust/pull/118538/)
|
||||
- [Add `$message_type` field to distinguish json diagnostic outputs](https://github.com/rust-lang/rust/pull/115691/)
|
||||
- [Enable Rust to use the EHCont security feature of Windows](https://github.com/rust-lang/rust/pull/118013/)
|
||||
- [Add tier 3 {x86_64,i686}-win7-windows-msvc targets](https://github.com/rust-lang/rust/pull/118150/)
|
||||
- [Add tier 3 aarch64-apple-watchos target](https://github.com/rust-lang/rust/pull/119074/)
|
||||
- [Add tier 3 arm64e-apple-ios & arm64e-apple-darwin targets](https://github.com/rust-lang/rust/pull/115526/)
|
||||
|
||||
Refer to Rust's [platform support page][platform-support-doc]
|
||||
for more information on Rust's tiered platform support.
|
||||
|
||||
<a id="1.76.0-Libraries"></a>
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [Add a column number to `dbg!()`](https://github.com/rust-lang/rust/pull/114962/)
|
||||
- [Add `std::hash::{DefaultHasher, RandomState}` exports](https://github.com/rust-lang/rust/pull/115694/)
|
||||
- [Fix rounding issue with exponents in fmt](https://github.com/rust-lang/rust/pull/116301/)
|
||||
- [Add T: ?Sized to `RwLockReadGuard` and `RwLockWriteGuard`'s Debug impls.](https://github.com/rust-lang/rust/pull/117138/)
|
||||
- [Windows: Allow `File::create` to work on hidden files](https://github.com/rust-lang/rust/pull/116438/)
|
||||
|
||||
<a id="1.76.0-Stabilized-APIs"></a>
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
- [`Arc::unwrap_or_clone`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.unwrap_or_clone)
|
||||
- [`Rc::unwrap_or_clone`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.unwrap_or_clone)
|
||||
- [`Result::inspect`](https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect)
|
||||
- [`Result::inspect_err`](https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect_err)
|
||||
- [`Option::inspect`](https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.inspect)
|
||||
- [`type_name_of_val`](https://doc.rust-lang.org/stable/std/any/fn.type_name_of_val.html)
|
||||
- [`std::hash::{DefaultHasher, RandomState}`](https://doc.rust-lang.org/stable/std/hash/index.html#structs)
|
||||
These were previously available only through `std::collections::hash_map`.
|
||||
- [`ptr::{from_ref, from_mut}`](https://doc.rust-lang.org/stable/std/ptr/fn.from_ref.html)
|
||||
- [`ptr::addr_eq`](https://doc.rust-lang.org/stable/std/ptr/fn.addr_eq.html)
|
||||
|
||||
<a id="1.76.0-Cargo"></a>
|
||||
|
||||
Cargo
|
||||
-----
|
||||
|
||||
See [Cargo release notes](https://github.com/rust-lang/cargo/blob/master/CHANGELOG.md#cargo-176-2024-02-08).
|
||||
|
||||
<a id="1.76.0-Rustdoc"></a>
|
||||
|
||||
Rustdoc
|
||||
-------
|
||||
|
||||
- [Don't merge cfg and doc(cfg) attributes for re-exports](https://github.com/rust-lang/rust/pull/113091/)
|
||||
- [rustdoc: allow resizing the sidebar / hiding the top bar](https://github.com/rust-lang/rust/pull/115660/)
|
||||
- [rustdoc-search: add support for traits and associated types](https://github.com/rust-lang/rust/pull/116085/)
|
||||
- [rustdoc: Add highlighting for comments in items declaration](https://github.com/rust-lang/rust/pull/117869/)
|
||||
|
||||
<a id="1.76.0-Compatibility-Notes"></a>
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Add allow-by-default lint for unit bindings](https://github.com/rust-lang/rust/pull/112380/)
|
||||
This is expected to be upgraded to a warning by default in a future Rust
|
||||
release. Some macros emit bindings with type `()` with user-provided spans,
|
||||
which means that this lint will warn for user code.
|
||||
- [Remove x86_64-sun-solaris target.](https://github.com/rust-lang/rust/pull/118091/)
|
||||
- [Remove asmjs-unknown-emscripten target](https://github.com/rust-lang/rust/pull/117338/)
|
||||
- [Report errors in jobserver inherited through environment variables](https://github.com/rust-lang/rust/pull/113730/)
|
||||
This [may warn](https://github.com/rust-lang/rust/issues/120515) on benign problems too.
|
||||
- [Update the minimum external LLVM to 16.](https://github.com/rust-lang/rust/pull/117947/)
|
||||
- [Improve `print_tts`](https://github.com/rust-lang/rust/pull/114571/)
|
||||
This change can break some naive manual parsing of token trees in proc macro
|
||||
code which expect a particular structure after `.to_string()`, rather than just arbitrary Rust code.
|
||||
- [Make `IMPLIED_BOUNDS_ENTAILMENT` into a hard error from a lint](https://github.com/rust-lang/rust/pull/117984/)
|
||||
- [Vec's allocation behavior was changed when collecting some iterators](https://github.com/rust-lang/rust/pull/110353)
|
||||
Allocation behavior is currently not specified, nevertheless changes can be surprising.
|
||||
See [`impl FromIterator for Vec`](https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#impl-FromIterator%3CT%3E-for-Vec%3CT%3E)
|
||||
for more details.
|
||||
- [Properly reject `default` on free const items](https://github.com/rust-lang/rust/pull/117818/)
|
||||
|
||||
Version 1.75.0 (2023-12-28)
|
||||
==========================
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ use crate::{
|
||||
pub trait LayoutCalculator {
|
||||
type TargetDataLayoutRef: Borrow<TargetDataLayout>;
|
||||
|
||||
fn delay_bug(&self, txt: String);
|
||||
fn delayed_bug(&self, txt: String);
|
||||
fn current_data_layout(&self) -> Self::TargetDataLayoutRef;
|
||||
|
||||
fn scalar_pair<FieldIdx: Idx, VariantIdx: Idx>(
|
||||
@ -111,8 +111,8 @@ pub trait LayoutCalculator {
|
||||
alt_tail_space,
|
||||
layout.fields.count(),
|
||||
prefer_alt_layout,
|
||||
format_field_niches(&layout, &fields, &dl),
|
||||
format_field_niches(&alt_layout, &fields, &dl),
|
||||
format_field_niches(layout, fields, dl),
|
||||
format_field_niches(&alt_layout, fields, dl),
|
||||
);
|
||||
|
||||
if prefer_alt_layout {
|
||||
@ -382,7 +382,7 @@ pub trait LayoutCalculator {
|
||||
*offset += this_offset;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
|
||||
panic!("Layout of fields should be Arbitrary for variants")
|
||||
}
|
||||
}
|
||||
@ -600,7 +600,9 @@ pub trait LayoutCalculator {
|
||||
variant.size = new_ity_size;
|
||||
}
|
||||
}
|
||||
_ => panic!(),
|
||||
FieldsShape::Primitive | FieldsShape::Array { .. } | FieldsShape::Union(..) => {
|
||||
panic!("encountered a non-arbitrary layout during enum layout")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -628,7 +630,7 @@ pub trait LayoutCalculator {
|
||||
let mut common_prim_initialized_in_all_variants = true;
|
||||
for (field_layouts, layout_variant) in iter::zip(variants, &layout_variants) {
|
||||
let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
|
||||
panic!();
|
||||
panic!("encountered a non-arbitrary layout during enum layout");
|
||||
};
|
||||
// We skip *all* ZST here and later check if we are good in terms of alignment.
|
||||
// This lets us handle some cases involving aligned ZST.
|
||||
@ -681,7 +683,7 @@ pub trait LayoutCalculator {
|
||||
assert_eq!(memory_index.raw, [0, 1]);
|
||||
offsets
|
||||
}
|
||||
_ => panic!(),
|
||||
_ => panic!("encountered a non-arbitrary layout during enum layout"),
|
||||
};
|
||||
if pair_offsets[FieldIdx::new(0)] == Size::ZERO
|
||||
&& pair_offsets[FieldIdx::new(1)] == *offset
|
||||
@ -758,7 +760,9 @@ pub trait LayoutCalculator {
|
||||
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
|
||||
Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants }
|
||||
}
|
||||
_ => panic!(),
|
||||
Variants::Single { .. } => {
|
||||
panic!("encountered a single-variant enum during multi-variant layout")
|
||||
}
|
||||
};
|
||||
Some(best_layout.layout)
|
||||
}
|
||||
@ -792,7 +796,7 @@ pub trait LayoutCalculator {
|
||||
let only_variant = &variants[VariantIdx::new(0)];
|
||||
for field in only_variant {
|
||||
if field.is_unsized() {
|
||||
self.delay_bug("unsized field in union".to_string());
|
||||
self.delayed_bug("unsized field in union".to_string());
|
||||
}
|
||||
|
||||
align = align.max(field.align);
|
||||
@ -1025,7 +1029,7 @@ fn univariant<
|
||||
// At the bottom of this function, we invert `inverse_memory_index` to
|
||||
// produce `memory_index` (see `invert_mapping`).
|
||||
let mut sized = true;
|
||||
let mut offsets = IndexVec::from_elem(Size::ZERO, &fields);
|
||||
let mut offsets = IndexVec::from_elem(Size::ZERO, fields);
|
||||
let mut offset = Size::ZERO;
|
||||
let mut largest_niche = None;
|
||||
let mut largest_niche_available = 0;
|
||||
@ -1038,7 +1042,7 @@ fn univariant<
|
||||
for &i in &inverse_memory_index {
|
||||
let field = &fields[i];
|
||||
if !sized {
|
||||
this.delay_bug(format!(
|
||||
this.delayed_bug(format!(
|
||||
"univariant: field #{} comes after unsized field",
|
||||
offsets.len(),
|
||||
));
|
||||
@ -1154,7 +1158,11 @@ fn univariant<
|
||||
assert_eq!(memory_index.raw, [0, 1]);
|
||||
offsets
|
||||
}
|
||||
_ => panic!(),
|
||||
FieldsShape::Primitive
|
||||
| FieldsShape::Array { .. }
|
||||
| FieldsShape::Union(..) => {
|
||||
panic!("encountered a non-arbitrary layout during enum layout")
|
||||
}
|
||||
};
|
||||
if offsets[i] == pair_offsets[FieldIdx::new(0)]
|
||||
&& offsets[j] == pair_offsets[FieldIdx::new(1)]
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#![cfg_attr(feature = "nightly", feature(step_trait))]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(all(not(bootstrap), feature = "nightly"), doc(rust_logo))]
|
||||
#![cfg_attr(all(not(bootstrap), feature = "nightly"), feature(rustdoc_internals))]
|
||||
#![cfg_attr(feature = "nightly", doc(rust_logo))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
|
||||
|
||||
use std::fmt;
|
||||
use std::num::{NonZeroUsize, ParseIntError};
|
||||
|
||||
@ -11,8 +11,8 @@
|
||||
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
|
||||
test(no_crate_inject, attr(deny(warnings)))
|
||||
)]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(new_uninit)]
|
||||
@ -197,23 +197,24 @@ impl<T> TypedArena<T> {
|
||||
start_ptr
|
||||
}
|
||||
|
||||
/// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`.
|
||||
///
|
||||
/// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before
|
||||
/// storing the elements in the arena.
|
||||
#[inline]
|
||||
pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
|
||||
// This implementation is entirely separate to
|
||||
// `DroplessIterator::alloc_from_iter`, even though conceptually they
|
||||
// are the same.
|
||||
// Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason
|
||||
// is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
|
||||
// reference to `self` and adding elements to the arena during iteration.
|
||||
//
|
||||
// `DroplessIterator` (in the fast case) writes elements from the
|
||||
// iterator one at a time into the allocated memory. That's easy
|
||||
// because the elements don't implement `Drop`. But for `TypedArena`
|
||||
// they do implement `Drop`, which means that if the iterator panics we
|
||||
// could end up with some allocated-but-uninitialized elements, which
|
||||
// will then cause UB in `TypedArena::drop`.
|
||||
// For this reason, if we pre-allocated any space for the elements of this iterator, we'd
|
||||
// have to track that some uninitialized elements are followed by some initialized elements,
|
||||
// else we might accidentally drop uninitialized memory if something panics or if the
|
||||
// iterator doesn't fill all the length we expected.
|
||||
//
|
||||
// Instead we use an approach where any iterator panic will occur
|
||||
// before the memory is allocated. This function is much less hot than
|
||||
// `DroplessArena::alloc_from_iter`, so it doesn't need to be
|
||||
// hyper-optimized.
|
||||
// So we collect all the elements beforehand, which takes care of reentrancy and panic
|
||||
// safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it
|
||||
// doesn't need to be hyper-optimized.
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
|
||||
let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
|
||||
@ -483,10 +484,25 @@ impl DroplessArena {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a string slice that is copied into the `DroplessArena`, returning a
|
||||
/// reference to it. Will panic if passed an empty string.
|
||||
///
|
||||
/// Panics:
|
||||
///
|
||||
/// - Zero-length string
|
||||
#[inline]
|
||||
pub fn alloc_str(&self, string: &str) -> &str {
|
||||
let slice = self.alloc_slice(string.as_bytes());
|
||||
|
||||
// SAFETY: the result has a copy of the same valid UTF-8 bytes.
|
||||
unsafe { std::str::from_utf8_unchecked(slice) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `mem` is valid for writes up to
|
||||
/// `size_of::<T>() * len`.
|
||||
/// The caller must ensure that `mem` is valid for writes up to `size_of::<T>() * len`, and that
|
||||
/// that memory stays allocated and not shared for the lifetime of `self`. This must hold even
|
||||
/// if `iter.next()` allocates onto `self`.
|
||||
#[inline]
|
||||
unsafe fn write_from_iter<T, I: Iterator<Item = T>>(
|
||||
&self,
|
||||
@ -516,6 +532,8 @@ impl DroplessArena {
|
||||
|
||||
#[inline]
|
||||
pub fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
|
||||
// Warning: this function is reentrant: `iter` could hold a reference to `&self` and
|
||||
// allocate additional elements while we're iterating.
|
||||
let iter = iter.into_iter();
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
assert!(!mem::needs_drop::<T>());
|
||||
@ -524,7 +542,7 @@ impl DroplessArena {
|
||||
|
||||
match size_hint {
|
||||
(min, Some(max)) if min == max => {
|
||||
// We know the exact number of elements the iterator will produce here
|
||||
// We know the exact number of elements the iterator expects to produce here.
|
||||
let len = min;
|
||||
|
||||
if len == 0 {
|
||||
@ -532,10 +550,15 @@ impl DroplessArena {
|
||||
}
|
||||
|
||||
let mem = self.alloc_raw(Layout::array::<T>(len).unwrap()) as *mut T;
|
||||
// SAFETY: `write_from_iter` doesn't touch `self`. It only touches the slice we just
|
||||
// reserved. If the iterator panics or doesn't output `len` elements, this will
|
||||
// leave some unallocated slots in the arena, which is fine because we do not call
|
||||
// `drop`.
|
||||
unsafe { self.write_from_iter(iter, len, mem) }
|
||||
}
|
||||
(_, _) => {
|
||||
outline(move || -> &mut [T] {
|
||||
// Takes care of reentrancy.
|
||||
let mut vec: SmallVec<[_; 8]> = iter.collect();
|
||||
if vec.is_empty() {
|
||||
return &mut [];
|
||||
@ -646,6 +669,14 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
|
||||
self.dropless.alloc_slice(value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn alloc_str(&self, string: &str) -> &str {
|
||||
if string.is_empty() {
|
||||
return "";
|
||||
}
|
||||
self.dropless.alloc_str(string)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_from_iter<T: ArenaAllocatable<'tcx, C>, C>(
|
||||
&self,
|
||||
|
||||
@ -301,7 +301,7 @@ pub enum TraitBoundModifier {
|
||||
Maybe,
|
||||
|
||||
/// `~const Trait`
|
||||
MaybeConst,
|
||||
MaybeConst(Span),
|
||||
|
||||
/// `~const !Trait`
|
||||
//
|
||||
@ -317,8 +317,7 @@ pub enum TraitBoundModifier {
|
||||
impl TraitBoundModifier {
|
||||
pub fn to_constness(self) -> Const {
|
||||
match self {
|
||||
// FIXME(effects) span
|
||||
Self::MaybeConst => Const::Yes(DUMMY_SP),
|
||||
Self::MaybeConst(span) => Const::Yes(span),
|
||||
_ => Const::No,
|
||||
}
|
||||
}
|
||||
@ -646,6 +645,7 @@ impl Pat {
|
||||
// These patterns do not contain subpatterns, skip.
|
||||
PatKind::Wild
|
||||
| PatKind::Rest
|
||||
| PatKind::Never
|
||||
| PatKind::Lit(_)
|
||||
| PatKind::Range(..)
|
||||
| PatKind::Ident(..)
|
||||
@ -658,6 +658,37 @@ impl Pat {
|
||||
pub fn is_rest(&self) -> bool {
|
||||
matches!(self.kind, PatKind::Rest)
|
||||
}
|
||||
|
||||
/// Whether this could be a never pattern, taking into account that a macro invocation can
|
||||
/// return a never pattern. Used to inform errors during parsing.
|
||||
pub fn could_be_never_pattern(&self) -> bool {
|
||||
let mut could_be_never_pattern = false;
|
||||
self.walk(&mut |pat| match &pat.kind {
|
||||
PatKind::Never | PatKind::MacCall(_) => {
|
||||
could_be_never_pattern = true;
|
||||
false
|
||||
}
|
||||
PatKind::Or(s) => {
|
||||
could_be_never_pattern = s.iter().all(|p| p.could_be_never_pattern());
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
});
|
||||
could_be_never_pattern
|
||||
}
|
||||
|
||||
/// Whether this contains a `!` pattern. This in particular means that a feature gate error will
|
||||
/// be raised if the feature is off. Used to avoid gating the feature twice.
|
||||
pub fn contains_never_pattern(&self) -> bool {
|
||||
let mut contains_never_pattern = false;
|
||||
self.walk(&mut |pat| {
|
||||
if matches!(pat.kind, PatKind::Never) {
|
||||
contains_never_pattern = true;
|
||||
}
|
||||
true
|
||||
});
|
||||
contains_never_pattern
|
||||
}
|
||||
}
|
||||
|
||||
/// A single field in a struct pattern.
|
||||
@ -796,6 +827,9 @@ pub enum PatKind {
|
||||
/// only one rest pattern may occur in the pattern sequences.
|
||||
Rest,
|
||||
|
||||
// A never pattern `!`
|
||||
Never,
|
||||
|
||||
/// Parentheses in patterns used for grouping (i.e., `(PAT)`).
|
||||
Paren(P<Pat>),
|
||||
|
||||
@ -818,7 +852,7 @@ pub enum BorrowKind {
|
||||
Raw,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum BinOpKind {
|
||||
/// The `+` operator (addition)
|
||||
Add,
|
||||
@ -859,9 +893,9 @@ pub enum BinOpKind {
|
||||
}
|
||||
|
||||
impl BinOpKind {
|
||||
pub fn to_string(&self) -> &'static str {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
use BinOpKind::*;
|
||||
match *self {
|
||||
match self {
|
||||
Add => "+",
|
||||
Sub => "-",
|
||||
Mul => "*",
|
||||
@ -882,19 +916,25 @@ impl BinOpKind {
|
||||
Gt => ">",
|
||||
}
|
||||
}
|
||||
pub fn lazy(&self) -> bool {
|
||||
|
||||
pub fn is_lazy(&self) -> bool {
|
||||
matches!(self, BinOpKind::And | BinOpKind::Or)
|
||||
}
|
||||
|
||||
pub fn is_comparison(&self) -> bool {
|
||||
use BinOpKind::*;
|
||||
// Note for developers: please keep this as is;
|
||||
// Note for developers: please keep this match exhaustive;
|
||||
// we want compilation to fail if another variant is added.
|
||||
match *self {
|
||||
Eq | Lt | Le | Ne | Gt | Ge => true,
|
||||
And | Or | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the binary operator takes its arguments by value.
|
||||
pub fn is_by_value(self) -> bool {
|
||||
!self.is_comparison()
|
||||
}
|
||||
}
|
||||
|
||||
pub type BinOp = Spanned<BinOpKind>;
|
||||
@ -902,7 +942,7 @@ pub type BinOp = Spanned<BinOpKind>;
|
||||
/// Unary operator.
|
||||
///
|
||||
/// Note that `&data` is not an operator, it's an `AddrOf` expression.
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Copy)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum UnOp {
|
||||
/// The `*` operator for dereferencing
|
||||
Deref,
|
||||
@ -913,13 +953,18 @@ pub enum UnOp {
|
||||
}
|
||||
|
||||
impl UnOp {
|
||||
pub fn to_string(op: UnOp) -> &'static str {
|
||||
match op {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
UnOp::Deref => "*",
|
||||
UnOp::Not => "!",
|
||||
UnOp::Neg => "-",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the unary operator takes its argument by value.
|
||||
pub fn is_by_value(self) -> bool {
|
||||
matches!(self, Self::Neg | Self::Not)
|
||||
}
|
||||
}
|
||||
|
||||
/// A statement
|
||||
@ -1066,8 +1111,8 @@ pub struct Arm {
|
||||
pub pat: P<Pat>,
|
||||
/// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`
|
||||
pub guard: Option<P<Expr>>,
|
||||
/// Match arm body.
|
||||
pub body: P<Expr>,
|
||||
/// Match arm body. Omitted if the pattern is a never pattern.
|
||||
pub body: Option<P<Expr>>,
|
||||
pub span: Span,
|
||||
pub id: NodeId,
|
||||
pub is_placeholder: bool,
|
||||
@ -1297,7 +1342,7 @@ pub struct Closure {
|
||||
pub binder: ClosureBinder,
|
||||
pub capture_clause: CaptureBy,
|
||||
pub constness: Const,
|
||||
pub asyncness: Async,
|
||||
pub coroutine_kind: Option<CoroutineKind>,
|
||||
pub movability: Movability,
|
||||
pub fn_decl: P<FnDecl>,
|
||||
pub body: P<Expr>,
|
||||
@ -1502,6 +1547,7 @@ pub enum ExprKind {
|
||||
pub enum GenBlockKind {
|
||||
Async,
|
||||
Gen,
|
||||
AsyncGen,
|
||||
}
|
||||
|
||||
impl fmt::Display for GenBlockKind {
|
||||
@ -1515,6 +1561,7 @@ impl GenBlockKind {
|
||||
match self {
|
||||
GenBlockKind::Async => "async",
|
||||
GenBlockKind::Gen => "gen",
|
||||
GenBlockKind::AsyncGen => "async gen",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2237,6 +2284,18 @@ pub enum InlineAsmOperand {
|
||||
},
|
||||
}
|
||||
|
||||
impl InlineAsmOperand {
|
||||
pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> {
|
||||
match self {
|
||||
Self::In { reg, .. }
|
||||
| Self::Out { reg, .. }
|
||||
| Self::InOut { reg, .. }
|
||||
| Self::SplitInOut { reg, .. } => Some(reg),
|
||||
Self::Const { .. } | Self::Sym { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inline assembly.
|
||||
///
|
||||
/// E.g., `asm!("NOP");`.
|
||||
@ -2380,28 +2439,47 @@ pub enum Unsafe {
|
||||
No,
|
||||
}
|
||||
|
||||
/// Describes what kind of coroutine markers, if any, a function has.
|
||||
///
|
||||
/// Coroutine markers are things that cause the function to generate a coroutine, such as `async`,
|
||||
/// which makes the function return `impl Future`, or `gen`, which makes the function return `impl
|
||||
/// Iterator`.
|
||||
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
||||
pub enum Async {
|
||||
Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
No,
|
||||
pub enum CoroutineKind {
|
||||
/// `async`, which returns an `impl Future`
|
||||
Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
/// `gen`, which returns an `impl Iterator`
|
||||
Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
/// `async gen`, which returns an `impl AsyncIterator`
|
||||
AsyncGen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
||||
pub enum Gen {
|
||||
Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
No,
|
||||
}
|
||||
|
||||
impl Async {
|
||||
impl CoroutineKind {
|
||||
pub fn is_async(self) -> bool {
|
||||
matches!(self, Async::Yes { .. })
|
||||
matches!(self, CoroutineKind::Async { .. })
|
||||
}
|
||||
|
||||
/// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item.
|
||||
pub fn opt_return_id(self) -> Option<(NodeId, Span)> {
|
||||
pub fn is_gen(self) -> bool {
|
||||
matches!(self, CoroutineKind::Gen { .. })
|
||||
}
|
||||
|
||||
pub fn closure_id(self) -> NodeId {
|
||||
match self {
|
||||
Async::Yes { return_impl_trait_id, span, .. } => Some((return_impl_trait_id, span)),
|
||||
Async::No => None,
|
||||
CoroutineKind::Async { closure_id, .. }
|
||||
| CoroutineKind::Gen { closure_id, .. }
|
||||
| CoroutineKind::AsyncGen { closure_id, .. } => closure_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// In this case this is an `async` or `gen` return, the `NodeId` for the generated `impl Trait`
|
||||
/// item.
|
||||
pub fn return_id(self) -> (NodeId, Span) {
|
||||
match self {
|
||||
CoroutineKind::Async { return_impl_trait_id, span, .. }
|
||||
| CoroutineKind::Gen { return_impl_trait_id, span, .. }
|
||||
| CoroutineKind::AsyncGen { return_impl_trait_id, span, .. } => {
|
||||
(return_impl_trait_id, span)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2574,7 +2652,7 @@ pub enum AttrStyle {
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[custom_encodable]
|
||||
#[orderable]
|
||||
#[debug_format = "AttrId({})"]
|
||||
pub struct AttrId {}
|
||||
}
|
||||
@ -2710,7 +2788,11 @@ pub enum VariantData {
|
||||
/// Struct variant.
|
||||
///
|
||||
/// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
|
||||
Struct(ThinVec<FieldDef>, bool),
|
||||
Struct {
|
||||
fields: ThinVec<FieldDef>,
|
||||
// FIXME: investigate making this a `Option<ErrorGuaranteed>`
|
||||
recovered: bool,
|
||||
},
|
||||
/// Tuple variant.
|
||||
///
|
||||
/// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.
|
||||
@ -2725,7 +2807,7 @@ impl VariantData {
|
||||
/// Return the fields of this variant.
|
||||
pub fn fields(&self) -> &[FieldDef] {
|
||||
match self {
|
||||
VariantData::Struct(fields, ..) | VariantData::Tuple(fields, _) => fields,
|
||||
VariantData::Struct { fields, .. } | VariantData::Tuple(fields, _) => fields,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
@ -2733,7 +2815,7 @@ impl VariantData {
|
||||
/// Return the `NodeId` of this variant's constructor, if it has one.
|
||||
pub fn ctor_node_id(&self) -> Option<NodeId> {
|
||||
match *self {
|
||||
VariantData::Struct(..) => None,
|
||||
VariantData::Struct { .. } => None,
|
||||
VariantData::Tuple(_, id) | VariantData::Unit(id) => Some(id),
|
||||
}
|
||||
}
|
||||
@ -2767,6 +2849,28 @@ impl Item {
|
||||
pub fn span_with_attributes(&self) -> Span {
|
||||
self.attrs.iter().fold(self.span, |acc, attr| acc.to(attr.span))
|
||||
}
|
||||
|
||||
pub fn opt_generics(&self) -> Option<&Generics> {
|
||||
match &self.kind {
|
||||
ItemKind::ExternCrate(_)
|
||||
| ItemKind::Use(_)
|
||||
| ItemKind::Mod(_, _)
|
||||
| ItemKind::ForeignMod(_)
|
||||
| ItemKind::GlobalAsm(_)
|
||||
| ItemKind::MacCall(_)
|
||||
| ItemKind::MacroDef(_) => None,
|
||||
ItemKind::Static(_) => None,
|
||||
ItemKind::Const(i) => Some(&i.generics),
|
||||
ItemKind::Fn(i) => Some(&i.generics),
|
||||
ItemKind::TyAlias(i) => Some(&i.generics),
|
||||
ItemKind::TraitAlias(generics, _)
|
||||
| ItemKind::Enum(_, generics)
|
||||
| ItemKind::Struct(_, generics)
|
||||
| ItemKind::Union(_, generics) => Some(&generics),
|
||||
ItemKind::Trait(i) => Some(&i.generics),
|
||||
ItemKind::Impl(i) => Some(&i.generics),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `extern` qualifier on a function item or function type.
|
||||
@ -2805,8 +2909,8 @@ impl Extern {
|
||||
pub struct FnHeader {
|
||||
/// The `unsafe` keyword, if any
|
||||
pub unsafety: Unsafe,
|
||||
/// The `async` keyword, if any
|
||||
pub asyncness: Async,
|
||||
/// Whether this is `async`, `gen`, or nothing.
|
||||
pub coroutine_kind: Option<CoroutineKind>,
|
||||
/// The `const` keyword, if any
|
||||
pub constness: Const,
|
||||
/// The `extern` keyword and corresponding ABI string, if any
|
||||
@ -2816,9 +2920,9 @@ pub struct FnHeader {
|
||||
impl FnHeader {
|
||||
/// Does this function header have any qualifiers or is it empty?
|
||||
pub fn has_qualifiers(&self) -> bool {
|
||||
let Self { unsafety, asyncness, constness, ext } = self;
|
||||
let Self { unsafety, coroutine_kind, constness, ext } = self;
|
||||
matches!(unsafety, Unsafe::Yes(_))
|
||||
|| asyncness.is_async()
|
||||
|| coroutine_kind.is_some()
|
||||
|| matches!(constness, Const::Yes(_))
|
||||
|| !matches!(ext, Extern::None)
|
||||
}
|
||||
@ -2828,7 +2932,7 @@ impl Default for FnHeader {
|
||||
fn default() -> FnHeader {
|
||||
FnHeader {
|
||||
unsafety: Unsafe::No,
|
||||
asyncness: Async::No,
|
||||
coroutine_kind: None,
|
||||
constness: Const::No,
|
||||
ext: Extern::None,
|
||||
}
|
||||
@ -3151,11 +3255,11 @@ mod size_asserts {
|
||||
static_assert_size!(Block, 32);
|
||||
static_assert_size!(Expr, 72);
|
||||
static_assert_size!(ExprKind, 40);
|
||||
static_assert_size!(Fn, 152);
|
||||
static_assert_size!(Fn, 160);
|
||||
static_assert_size!(ForeignItem, 96);
|
||||
static_assert_size!(ForeignItemKind, 24);
|
||||
static_assert_size!(GenericArg, 24);
|
||||
static_assert_size!(GenericBound, 56);
|
||||
static_assert_size!(GenericBound, 64);
|
||||
static_assert_size!(Generics, 40);
|
||||
static_assert_size!(Impl, 136);
|
||||
static_assert_size!(Item, 136);
|
||||
|
||||
@ -342,7 +342,7 @@ impl MetaItem {
|
||||
let span = span.with_hi(segments.last().unwrap().ident.span.hi());
|
||||
Path { span, segments, tokens: None }
|
||||
}
|
||||
Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt {
|
||||
Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &nt.0 {
|
||||
token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
|
||||
token::Nonterminal::NtPath(path) => (**path).clone(),
|
||||
_ => return None,
|
||||
@ -387,11 +387,11 @@ impl MetaItemKind {
|
||||
tokens: &mut impl Iterator<Item = &'a TokenTree>,
|
||||
) -> Option<MetaItemKind> {
|
||||
match tokens.next() {
|
||||
Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
|
||||
Some(TokenTree::Delimited(.., Delimiter::Invisible, inner_tokens)) => {
|
||||
MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees())
|
||||
}
|
||||
Some(TokenTree::Token(token, _)) => {
|
||||
MetaItemLit::from_token(&token).map(MetaItemKind::NameValue)
|
||||
MetaItemLit::from_token(token).map(MetaItemKind::NameValue)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@ -401,7 +401,7 @@ impl MetaItemKind {
|
||||
tokens: &mut iter::Peekable<impl Iterator<Item = &'a TokenTree>>,
|
||||
) -> Option<MetaItemKind> {
|
||||
match tokens.peek() {
|
||||
Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
|
||||
Some(TokenTree::Delimited(.., Delimiter::Parenthesis, inner_tokens)) => {
|
||||
let inner_tokens = inner_tokens.clone();
|
||||
tokens.next();
|
||||
MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
|
||||
@ -524,7 +524,7 @@ impl NestedMetaItem {
|
||||
tokens.next();
|
||||
return Some(NestedMetaItem::Lit(lit));
|
||||
}
|
||||
Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
|
||||
Some(TokenTree::Delimited(.., Delimiter::Invisible, inner_tokens)) => {
|
||||
tokens.next();
|
||||
return NestedMetaItem::from_tokens(&mut inner_tokens.trees().peekable());
|
||||
}
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
|
||||
test(attr(deny(warnings)))
|
||||
)]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![doc(rust_logo)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(const_trait_impl)]
|
||||
@ -59,9 +59,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
/// instead of implementing everything in `rustc_middle`.
|
||||
pub trait HashStableContext:
|
||||
rustc_type_ir::HashStableContext + rustc_span::HashStableContext
|
||||
{
|
||||
pub trait HashStableContext: rustc_span::HashStableContext {
|
||||
fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher);
|
||||
}
|
||||
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
//! a `MutVisitor` renaming item names in a module will miss all of those
|
||||
//! that are created by the expansion of a macro.
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::ptr::P;
|
||||
use crate::token::{self, Token};
|
||||
use crate::tokenstream::*;
|
||||
use crate::{ast::*, StaticItem};
|
||||
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
@ -121,8 +121,8 @@ pub trait MutVisitor: Sized {
|
||||
noop_visit_fn_decl(d, self);
|
||||
}
|
||||
|
||||
fn visit_asyncness(&mut self, a: &mut Async) {
|
||||
noop_visit_asyncness(a, self);
|
||||
fn visit_coroutine_kind(&mut self, a: &mut CoroutineKind) {
|
||||
noop_visit_coroutine_kind(a, self);
|
||||
}
|
||||
|
||||
fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
|
||||
@ -453,7 +453,7 @@ pub fn noop_flat_map_arm<T: MutVisitor>(mut arm: Arm, vis: &mut T) -> SmallVec<[
|
||||
vis.visit_id(id);
|
||||
vis.visit_pat(pat);
|
||||
visit_opt(guard, |guard| vis.visit_expr(guard));
|
||||
vis.visit_expr(body);
|
||||
visit_opt(body, |body| vis.visit_expr(body));
|
||||
vis.visit_span(span);
|
||||
smallvec![arm]
|
||||
}
|
||||
@ -682,7 +682,7 @@ pub fn visit_attr_tt<T: MutVisitor>(tt: &mut AttrTokenTree, vis: &mut T) {
|
||||
AttrTokenTree::Token(token, _) => {
|
||||
visit_token(token, vis);
|
||||
}
|
||||
AttrTokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => {
|
||||
AttrTokenTree::Delimited(DelimSpan { open, close }, _spacing, _delim, tts) => {
|
||||
vis.visit_span(open);
|
||||
vis.visit_span(close);
|
||||
visit_attr_tts(tts, vis);
|
||||
@ -709,7 +709,7 @@ pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
|
||||
TokenTree::Token(token, _) => {
|
||||
visit_token(token, vis);
|
||||
}
|
||||
TokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => {
|
||||
TokenTree::Delimited(DelimSpan { open, close }, _spacing, _delim, tts) => {
|
||||
vis.visit_span(open);
|
||||
vis.visit_span(close);
|
||||
visit_tts(tts, vis);
|
||||
@ -764,7 +764,10 @@ pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
|
||||
return; // Avoid visiting the span for the second time.
|
||||
}
|
||||
token::Interpolated(nt) => {
|
||||
visit_nonterminal(Lrc::make_mut(nt), vis);
|
||||
let nt = Lrc::make_mut(nt);
|
||||
let (nt, sp) = (&mut nt.0, &mut nt.1);
|
||||
vis.visit_span(sp);
|
||||
visit_nonterminal(nt, vis);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -868,13 +871,15 @@ pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis:
|
||||
}
|
||||
}
|
||||
|
||||
pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) {
|
||||
match asyncness {
|
||||
Async::Yes { span: _, closure_id, return_impl_trait_id } => {
|
||||
pub fn noop_visit_coroutine_kind<T: MutVisitor>(coroutine_kind: &mut CoroutineKind, vis: &mut T) {
|
||||
match coroutine_kind {
|
||||
CoroutineKind::Async { span, closure_id, return_impl_trait_id }
|
||||
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id }
|
||||
| CoroutineKind::AsyncGen { span, closure_id, return_impl_trait_id } => {
|
||||
vis.visit_span(span);
|
||||
vis.visit_id(closure_id);
|
||||
vis.visit_id(return_impl_trait_id);
|
||||
}
|
||||
Async::No => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -971,7 +976,7 @@ pub fn noop_visit_where_predicate<T: MutVisitor>(pred: &mut WherePredicate, vis:
|
||||
|
||||
pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T) {
|
||||
match vdata {
|
||||
VariantData::Struct(fields, ..) => {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
|
||||
}
|
||||
VariantData::Tuple(fields, id) => {
|
||||
@ -1167,9 +1172,9 @@ fn visit_const_item<T: MutVisitor>(
|
||||
}
|
||||
|
||||
pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
|
||||
let FnHeader { unsafety, asyncness, constness, ext: _ } = header;
|
||||
let FnHeader { unsafety, coroutine_kind, constness, ext: _ } = header;
|
||||
visit_constness(constness, vis);
|
||||
vis.visit_asyncness(asyncness);
|
||||
coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind));
|
||||
visit_unsafety(unsafety, vis);
|
||||
}
|
||||
|
||||
@ -1246,7 +1251,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
|
||||
let Pat { id, kind, span, tokens } = pat.deref_mut();
|
||||
vis.visit_id(id);
|
||||
match kind {
|
||||
PatKind::Wild | PatKind::Rest => {}
|
||||
PatKind::Wild | PatKind::Rest | PatKind::Never => {}
|
||||
PatKind::Ident(_binding_mode, ident, sub) => {
|
||||
vis.visit_ident(ident);
|
||||
visit_opt(sub, |sub| vis.visit_pat(sub));
|
||||
@ -1403,7 +1408,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||
binder,
|
||||
capture_clause,
|
||||
constness,
|
||||
asyncness,
|
||||
coroutine_kind,
|
||||
movability: _,
|
||||
fn_decl,
|
||||
body,
|
||||
@ -1412,7 +1417,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||
}) => {
|
||||
vis.visit_closure_binder(binder);
|
||||
visit_constness(constness, vis);
|
||||
vis.visit_asyncness(asyncness);
|
||||
coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind));
|
||||
vis.visit_capture_by(capture_clause);
|
||||
vis.visit_fn_decl(fn_decl);
|
||||
vis.visit_expr(body);
|
||||
|
||||
@ -8,6 +8,8 @@ rustc_index::newtype_index! {
|
||||
/// This is later turned into [`DefId`] and `HirId` for the HIR.
|
||||
///
|
||||
/// [`DefId`]: rustc_span::def_id::DefId
|
||||
#[encodable]
|
||||
#[orderable]
|
||||
#[debug_format = "NodeId({})"]
|
||||
pub struct NodeId {
|
||||
/// The [`NodeId`] used to represent the root of the crate.
|
||||
|
||||
@ -13,7 +13,7 @@ use rustc_macros::HashStable_Generic;
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
#[allow(hidden_glob_reexports)]
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::{self, edition::Edition, Span, DUMMY_SP};
|
||||
use rustc_span::{edition::Edition, Span, DUMMY_SP};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
@ -110,7 +110,7 @@ impl Lit {
|
||||
Ident(name, false) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
|
||||
Literal(token_lit) => Some(token_lit),
|
||||
Interpolated(ref nt)
|
||||
if let NtExpr(expr) | NtLiteral(expr) = &**nt
|
||||
if let NtExpr(expr) | NtLiteral(expr) = &nt.0
|
||||
&& let ast::ExprKind::Lit(token_lit) = expr.kind =>
|
||||
{
|
||||
Some(token_lit)
|
||||
@ -238,9 +238,9 @@ pub enum TokenKind {
|
||||
EqEq,
|
||||
/// `!=`
|
||||
Ne,
|
||||
/// `>`
|
||||
Ge,
|
||||
/// `>=`
|
||||
Ge,
|
||||
/// `>`
|
||||
Gt,
|
||||
/// `&&`
|
||||
AndAnd,
|
||||
@ -314,7 +314,7 @@ pub enum TokenKind {
|
||||
/// - It prevents `Token` from implementing `Copy`.
|
||||
/// It adds complexity and likely slows things down. Please don't add new
|
||||
/// occurrences of this token kind!
|
||||
Interpolated(Lrc<Nonterminal>),
|
||||
Interpolated(Lrc<(Nonterminal, Span)>),
|
||||
|
||||
/// A doc comment token.
|
||||
/// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
|
||||
@ -388,7 +388,8 @@ impl TokenKind {
|
||||
match *self {
|
||||
Comma => Some(vec![Dot, Lt, Semi]),
|
||||
Semi => Some(vec![Colon, Comma]),
|
||||
FatArrow => Some(vec![Eq, RArrow]),
|
||||
Colon => Some(vec![Semi]),
|
||||
FatArrow => Some(vec![Eq, RArrow, Ge, Gt]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -421,7 +422,7 @@ impl Token {
|
||||
/// if they keep spans or perform edition checks.
|
||||
pub fn uninterpolated_span(&self) -> Span {
|
||||
match &self.kind {
|
||||
Interpolated(nt) => nt.span(),
|
||||
Interpolated(nt) => nt.0.use_span(),
|
||||
_ => self.span,
|
||||
}
|
||||
}
|
||||
@ -464,7 +465,7 @@ impl Token {
|
||||
ModSep | // global path
|
||||
Lifetime(..) | // labeled loop
|
||||
Pound => true, // expression attributes
|
||||
Interpolated(ref nt) => matches!(**nt, NtLiteral(..) |
|
||||
Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) |
|
||||
NtExpr(..) |
|
||||
NtBlock(..) |
|
||||
NtPath(..)),
|
||||
@ -488,7 +489,7 @@ impl Token {
|
||||
| DotDot | DotDotDot | DotDotEq // ranges
|
||||
| Lt | BinOp(Shl) // associated path
|
||||
| ModSep => true, // global path
|
||||
Interpolated(ref nt) => matches!(**nt, NtLiteral(..) |
|
||||
Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) |
|
||||
NtPat(..) |
|
||||
NtBlock(..) |
|
||||
NtPath(..)),
|
||||
@ -511,7 +512,7 @@ impl Token {
|
||||
Lifetime(..) | // lifetime bound in trait object
|
||||
Lt | BinOp(Shl) | // associated path
|
||||
ModSep => true, // global path
|
||||
Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
|
||||
Interpolated(ref nt) => matches!(&nt.0, NtTy(..) | NtPath(..)),
|
||||
// For anonymous structs or unions, which only appear in specific positions
|
||||
// (type of struct fields or union fields), we don't consider them as regular types
|
||||
_ => false,
|
||||
@ -522,7 +523,7 @@ impl Token {
|
||||
pub fn can_begin_const_arg(&self) -> bool {
|
||||
match self.kind {
|
||||
OpenDelim(Delimiter::Brace) => true,
|
||||
Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
|
||||
Interpolated(ref nt) => matches!(&nt.0, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
|
||||
_ => self.can_begin_literal_maybe_minus(),
|
||||
}
|
||||
}
|
||||
@ -576,7 +577,7 @@ impl Token {
|
||||
match self.uninterpolate().kind {
|
||||
Literal(..) | BinOp(Minus) => true,
|
||||
Ident(name, false) if name.is_bool_lit() => true,
|
||||
Interpolated(ref nt) => match &**nt {
|
||||
Interpolated(ref nt) => match &nt.0 {
|
||||
NtLiteral(_) => true,
|
||||
NtExpr(e) => match &e.kind {
|
||||
ast::ExprKind::Lit(_) => true,
|
||||
@ -597,9 +598,9 @@ impl Token {
|
||||
/// otherwise returns the original token.
|
||||
pub fn uninterpolate(&self) -> Cow<'_, Token> {
|
||||
match &self.kind {
|
||||
Interpolated(nt) => match **nt {
|
||||
Interpolated(nt) => match &nt.0 {
|
||||
NtIdent(ident, is_raw) => {
|
||||
Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span))
|
||||
Cow::Owned(Token::new(Ident(ident.name, *is_raw), ident.span))
|
||||
}
|
||||
NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)),
|
||||
_ => Cow::Borrowed(self),
|
||||
@ -614,8 +615,8 @@ impl Token {
|
||||
// We avoid using `Token::uninterpolate` here because it's slow.
|
||||
match &self.kind {
|
||||
&Ident(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
|
||||
Interpolated(nt) => match **nt {
|
||||
NtIdent(ident, is_raw) => Some((ident, is_raw)),
|
||||
Interpolated(nt) => match &nt.0 {
|
||||
NtIdent(ident, is_raw) => Some((*ident, *is_raw)),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
@ -628,8 +629,8 @@ impl Token {
|
||||
// We avoid using `Token::uninterpolate` here because it's slow.
|
||||
match &self.kind {
|
||||
&Lifetime(name) => Some(Ident::new(name, self.span)),
|
||||
Interpolated(nt) => match **nt {
|
||||
NtLifetime(ident) => Some(ident),
|
||||
Interpolated(nt) => match &nt.0 {
|
||||
NtLifetime(ident) => Some(*ident),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
@ -655,7 +656,7 @@ impl Token {
|
||||
/// Returns `true` if the token is an interpolated path.
|
||||
fn is_path(&self) -> bool {
|
||||
if let Interpolated(nt) = &self.kind
|
||||
&& let NtPath(..) = **nt
|
||||
&& let NtPath(..) = &nt.0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -668,7 +669,7 @@ impl Token {
|
||||
/// (which happens while parsing the result of macro expansion)?
|
||||
pub fn is_whole_expr(&self) -> bool {
|
||||
if let Interpolated(nt) = &self.kind
|
||||
&& let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt
|
||||
&& let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = &nt.0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -679,7 +680,7 @@ impl Token {
|
||||
/// Is the token an interpolated block (`$b:block`)?
|
||||
pub fn is_whole_block(&self) -> bool {
|
||||
if let Interpolated(nt) = &self.kind
|
||||
&& let NtBlock(..) = **nt
|
||||
&& let NtBlock(..) = &nt.0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -755,6 +756,11 @@ impl Token {
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if the token is the integer literal.
|
||||
pub fn is_integer_lit(&self) -> bool {
|
||||
matches!(self.kind, Literal(Lit { kind: LitKind::Integer, .. }))
|
||||
}
|
||||
|
||||
/// Returns `true` if the token is a non-raw identifier for which `pred` holds.
|
||||
pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
|
||||
match self.ident() {
|
||||
@ -927,7 +933,7 @@ impl fmt::Display for NonterminalKind {
|
||||
}
|
||||
|
||||
impl Nonterminal {
|
||||
pub fn span(&self) -> Span {
|
||||
pub fn use_span(&self) -> Span {
|
||||
match self {
|
||||
NtItem(item) => item.span,
|
||||
NtBlock(block) => block.span,
|
||||
@ -941,6 +947,23 @@ impl Nonterminal {
|
||||
NtVis(vis) => vis.span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn descr(&self) -> &'static str {
|
||||
match self {
|
||||
NtItem(..) => "item",
|
||||
NtBlock(..) => "block",
|
||||
NtStmt(..) => "statement",
|
||||
NtPat(..) => "pattern",
|
||||
NtExpr(..) => "expression",
|
||||
NtLiteral(..) => "literal",
|
||||
NtTy(..) => "type",
|
||||
NtIdent(..) => "identifier",
|
||||
NtLifetime(..) => "lifetime",
|
||||
NtMeta(..) => "attribute",
|
||||
NtPath(..) => "path",
|
||||
NtVis(..) => "visibility",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Nonterminal {
|
||||
|
||||
@ -46,7 +46,7 @@ pub enum TokenTree {
|
||||
/// delimiters are implicitly represented by `Delimited`.
|
||||
Token(Token, Spacing),
|
||||
/// A delimited sequence of token trees.
|
||||
Delimited(DelimSpan, Delimiter, TokenStream),
|
||||
Delimited(DelimSpan, DelimSpacing, Delimiter, TokenStream),
|
||||
}
|
||||
|
||||
// Ensure all fields of `TokenTree` are `DynSend` and `DynSync`.
|
||||
@ -62,11 +62,11 @@ where
|
||||
}
|
||||
|
||||
impl TokenTree {
|
||||
/// Checks if this `TokenTree` is equal to the other, regardless of span information.
|
||||
/// Checks if this `TokenTree` is equal to the other, regardless of span/spacing information.
|
||||
pub fn eq_unspanned(&self, other: &TokenTree) -> bool {
|
||||
match (self, other) {
|
||||
(TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
|
||||
(TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => {
|
||||
(TokenTree::Delimited(.., delim, tts), TokenTree::Delimited(.., delim2, tts2)) => {
|
||||
delim == delim2 && tts.eq_unspanned(tts2)
|
||||
}
|
||||
_ => false,
|
||||
@ -99,6 +99,11 @@ impl TokenTree {
|
||||
TokenTree::Token(Token::new(kind, span), Spacing::Joint)
|
||||
}
|
||||
|
||||
/// Create a `TokenTree::Token` with joint-hidden spacing.
|
||||
pub fn token_joint_hidden(kind: TokenKind, span: Span) -> TokenTree {
|
||||
TokenTree::Token(Token::new(kind, span), Spacing::JointHidden)
|
||||
}
|
||||
|
||||
pub fn uninterpolate(&self) -> Cow<'_, TokenTree> {
|
||||
match self {
|
||||
TokenTree::Token(token, spacing) => match token.uninterpolate() {
|
||||
@ -183,7 +188,7 @@ pub struct AttrTokenStream(pub Lrc<Vec<AttrTokenTree>>);
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
pub enum AttrTokenTree {
|
||||
Token(Token, Spacing),
|
||||
Delimited(DelimSpan, Delimiter, AttrTokenStream),
|
||||
Delimited(DelimSpan, DelimSpacing, Delimiter, AttrTokenStream),
|
||||
/// Stores the attributes for an attribute target,
|
||||
/// along with the tokens for that attribute target.
|
||||
/// See `AttributesData` for more information
|
||||
@ -208,9 +213,14 @@ impl AttrTokenStream {
|
||||
AttrTokenTree::Token(inner, spacing) => {
|
||||
smallvec![TokenTree::Token(inner.clone(), *spacing)].into_iter()
|
||||
}
|
||||
AttrTokenTree::Delimited(span, delim, stream) => {
|
||||
smallvec![TokenTree::Delimited(*span, *delim, stream.to_tokenstream()),]
|
||||
.into_iter()
|
||||
AttrTokenTree::Delimited(span, spacing, delim, stream) => {
|
||||
smallvec![TokenTree::Delimited(
|
||||
*span,
|
||||
*spacing,
|
||||
*delim,
|
||||
stream.to_tokenstream()
|
||||
),]
|
||||
.into_iter()
|
||||
}
|
||||
AttrTokenTree::Attributes(data) => {
|
||||
let idx = data
|
||||
@ -230,7 +240,7 @@ impl AttrTokenStream {
|
||||
let mut found = false;
|
||||
// Check the last two trees (to account for a trailing semi)
|
||||
for tree in target_tokens.iter_mut().rev().take(2) {
|
||||
if let TokenTree::Delimited(span, delim, delim_tokens) = tree {
|
||||
if let TokenTree::Delimited(span, spacing, delim, delim_tokens) = tree {
|
||||
// Inner attributes are only supported on extern blocks, functions,
|
||||
// impls, and modules. All of these have their inner attributes
|
||||
// placed at the beginning of the rightmost outermost braced group:
|
||||
@ -250,7 +260,7 @@ impl AttrTokenStream {
|
||||
stream.push_stream(inner_attr.tokens());
|
||||
}
|
||||
stream.push_stream(delim_tokens.clone());
|
||||
*tree = TokenTree::Delimited(*span, *delim, stream);
|
||||
*tree = TokenTree::Delimited(*span, *spacing, *delim, stream);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -303,21 +313,64 @@ pub struct AttributesData {
|
||||
#[derive(Clone, Debug, Default, Encodable, Decodable)]
|
||||
pub struct TokenStream(pub(crate) Lrc<Vec<TokenTree>>);
|
||||
|
||||
/// Similar to `proc_macro::Spacing`, but for tokens.
|
||||
///
|
||||
/// Note that all `ast::TokenTree::Token` instances have a `Spacing`, but when
|
||||
/// we convert to `proc_macro::TokenTree` for proc macros only `Punct`
|
||||
/// `TokenTree`s have a `proc_macro::Spacing`.
|
||||
/// Indicates whether a token can join with the following token to form a
|
||||
/// compound token. Used for conversions to `proc_macro::Spacing`. Also used to
|
||||
/// guide pretty-printing, which is where the `JointHidden` value (which isn't
|
||||
/// part of `proc_macro::Spacing`) comes in useful.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum Spacing {
|
||||
/// The token is not immediately followed by an operator token (as
|
||||
/// determined by `Token::is_op`). E.g. a `+` token is `Alone` in `+ =`,
|
||||
/// `+/*foo*/=`, `+ident`, and `+()`.
|
||||
/// The token cannot join with the following token to form a compound
|
||||
/// token.
|
||||
///
|
||||
/// In token streams parsed from source code, the compiler will use `Alone`
|
||||
/// for any token immediately followed by whitespace, a non-doc comment, or
|
||||
/// EOF.
|
||||
///
|
||||
/// When constructing token streams within the compiler, use this for each
|
||||
/// token that (a) should be pretty-printed with a space after it, or (b)
|
||||
/// is the last token in the stream. (In the latter case the choice of
|
||||
/// spacing doesn't matter because it is never used for the last token. We
|
||||
/// arbitrarily use `Alone`.)
|
||||
///
|
||||
/// Converts to `proc_macro::Spacing::Alone`, and
|
||||
/// `proc_macro::Spacing::Alone` converts back to this.
|
||||
Alone,
|
||||
|
||||
/// The token is immediately followed by an operator token. E.g. a `+`
|
||||
/// token is `Joint` in `+=` and `++`.
|
||||
/// The token can join with the following token to form a compound token.
|
||||
///
|
||||
/// In token streams parsed from source code, the compiler will use `Joint`
|
||||
/// for any token immediately followed by punctuation (as determined by
|
||||
/// `Token::is_punct`).
|
||||
///
|
||||
/// When constructing token streams within the compiler, use this for each
|
||||
/// token that (a) should be pretty-printed without a space after it, and
|
||||
/// (b) is followed by a punctuation token.
|
||||
///
|
||||
/// Converts to `proc_macro::Spacing::Joint`, and
|
||||
/// `proc_macro::Spacing::Joint` converts back to this.
|
||||
Joint,
|
||||
|
||||
/// The token can join with the following token to form a compound token,
|
||||
/// but this will not be visible at the proc macro level. (This is what the
|
||||
/// `Hidden` means; see below.)
|
||||
///
|
||||
/// In token streams parsed from source code, the compiler will use
|
||||
/// `JointHidden` for any token immediately followed by anything not
|
||||
/// covered by the `Alone` and `Joint` cases: an identifier, lifetime,
|
||||
/// literal, delimiter, doc comment.
|
||||
///
|
||||
/// When constructing token streams, use this for each token that (a)
|
||||
/// should be pretty-printed without a space after it, and (b) is followed
|
||||
/// by a non-punctuation token.
|
||||
///
|
||||
/// Converts to `proc_macro::Spacing::Alone`, but
|
||||
/// `proc_macro::Spacing::Alone` converts back to `token::Spacing::Alone`.
|
||||
/// Because of that, pretty-printing of `TokenStream`s produced by proc
|
||||
/// macros is unavoidably uglier (with more whitespace between tokens) than
|
||||
/// pretty-printing of `TokenStream`'s produced by other means (i.e. parsed
|
||||
/// source code, internally constructed token streams, and token streams
|
||||
/// produced by declarative macros).
|
||||
JointHidden,
|
||||
}
|
||||
|
||||
impl TokenStream {
|
||||
@ -421,21 +474,14 @@ impl TokenStream {
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a token stream containing a single token with alone spacing.
|
||||
/// Create a token stream containing a single token with alone spacing. The
|
||||
/// spacing used for the final token in a constructed stream doesn't matter
|
||||
/// because it's never used. In practice we arbitrarily use
|
||||
/// `Spacing::Alone`.
|
||||
pub fn token_alone(kind: TokenKind, span: Span) -> TokenStream {
|
||||
TokenStream::new(vec![TokenTree::token_alone(kind, span)])
|
||||
}
|
||||
|
||||
/// Create a token stream containing a single token with joint spacing.
|
||||
pub fn token_joint(kind: TokenKind, span: Span) -> TokenStream {
|
||||
TokenStream::new(vec![TokenTree::token_joint(kind, span)])
|
||||
}
|
||||
|
||||
/// Create a token stream containing a single `Delimited`.
|
||||
pub fn delimited(span: DelimSpan, delim: Delimiter, tts: TokenStream) -> TokenStream {
|
||||
TokenStream::new(vec![TokenTree::Delimited(span, delim, tts)])
|
||||
}
|
||||
|
||||
pub fn from_ast(node: &(impl HasAttrs + HasSpan + HasTokens + fmt::Debug)) -> TokenStream {
|
||||
let Some(tokens) = node.tokens() else {
|
||||
panic!("missing tokens for node at {:?}: {:?}", node.span(), node);
|
||||
@ -477,13 +523,14 @@ impl TokenStream {
|
||||
|
||||
fn flatten_token(token: &Token, spacing: Spacing) -> TokenTree {
|
||||
match &token.kind {
|
||||
token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = **nt => {
|
||||
token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = nt.0 => {
|
||||
TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing)
|
||||
}
|
||||
token::Interpolated(nt) => TokenTree::Delimited(
|
||||
DelimSpan::from_single(token.span),
|
||||
DelimSpacing::new(Spacing::JointHidden, spacing),
|
||||
Delimiter::Invisible,
|
||||
TokenStream::from_nonterminal_ast(nt).flattened(),
|
||||
TokenStream::from_nonterminal_ast(&nt.0).flattened(),
|
||||
),
|
||||
_ => TokenTree::Token(token.clone(), spacing),
|
||||
}
|
||||
@ -492,8 +539,8 @@ impl TokenStream {
|
||||
fn flatten_token_tree(tree: &TokenTree) -> TokenTree {
|
||||
match tree {
|
||||
TokenTree::Token(token, spacing) => TokenStream::flatten_token(token, *spacing),
|
||||
TokenTree::Delimited(span, delim, tts) => {
|
||||
TokenTree::Delimited(*span, *delim, tts.flattened())
|
||||
TokenTree::Delimited(span, spacing, delim, tts) => {
|
||||
TokenTree::Delimited(*span, *spacing, *delim, tts.flattened())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -503,7 +550,7 @@ impl TokenStream {
|
||||
fn can_skip(stream: &TokenStream) -> bool {
|
||||
stream.trees().all(|tree| match tree {
|
||||
TokenTree::Token(token, _) => !matches!(token.kind, token::Interpolated(_)),
|
||||
TokenTree::Delimited(_, _, inner) => can_skip(inner),
|
||||
TokenTree::Delimited(.., inner) => can_skip(inner),
|
||||
})
|
||||
}
|
||||
|
||||
@ -517,7 +564,7 @@ impl TokenStream {
|
||||
// If `vec` is not empty, try to glue `tt` onto its last token. The return
|
||||
// value indicates if gluing took place.
|
||||
fn try_glue_to_last(vec: &mut Vec<TokenTree>, tt: &TokenTree) -> bool {
|
||||
if let Some(TokenTree::Token(last_tok, Spacing::Joint)) = vec.last()
|
||||
if let Some(TokenTree::Token(last_tok, Spacing::Joint | Spacing::JointHidden)) = vec.last()
|
||||
&& let TokenTree::Token(tok, spacing) = tt
|
||||
&& let Some(glued_tok) = last_tok.glue(tok)
|
||||
{
|
||||
@ -592,9 +639,10 @@ impl TokenStream {
|
||||
|
||||
&TokenTree::Token(..) => i += 1,
|
||||
|
||||
&TokenTree::Delimited(sp, delim, ref delim_stream) => {
|
||||
&TokenTree::Delimited(sp, spacing, delim, ref delim_stream) => {
|
||||
if let Some(desugared_delim_stream) = desugar_inner(delim_stream.clone()) {
|
||||
let new_tt = TokenTree::Delimited(sp, delim, desugared_delim_stream);
|
||||
let new_tt =
|
||||
TokenTree::Delimited(sp, spacing, delim, desugared_delim_stream);
|
||||
Lrc::make_mut(&mut stream.0)[i] = new_tt;
|
||||
modified = true;
|
||||
}
|
||||
@ -622,10 +670,11 @@ impl TokenStream {
|
||||
num_of_hashes = cmp::max(num_of_hashes, count);
|
||||
}
|
||||
|
||||
// `/// foo` becomes `doc = r"foo"`.
|
||||
// `/// foo` becomes `[doc = r"foo"]`.
|
||||
let delim_span = DelimSpan::from_single(span);
|
||||
let body = TokenTree::Delimited(
|
||||
delim_span,
|
||||
DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
|
||||
Delimiter::Bracket,
|
||||
[
|
||||
TokenTree::token_alone(token::Ident(sym::doc, false), span),
|
||||
@ -641,7 +690,7 @@ impl TokenStream {
|
||||
|
||||
if attr_style == AttrStyle::Inner {
|
||||
vec![
|
||||
TokenTree::token_alone(token::Pound, span),
|
||||
TokenTree::token_joint(token::Pound, span),
|
||||
TokenTree::token_alone(token::Not, span),
|
||||
body,
|
||||
]
|
||||
@ -738,6 +787,18 @@ impl DelimSpan {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
pub struct DelimSpacing {
|
||||
pub open: Spacing,
|
||||
pub close: Spacing,
|
||||
}
|
||||
|
||||
impl DelimSpacing {
|
||||
pub fn new(open: Spacing, close: Spacing) -> DelimSpacing {
|
||||
DelimSpacing { open, close }
|
||||
}
|
||||
}
|
||||
|
||||
// Some types are used a lot. Make sure they don't unintentionally get bigger.
|
||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||
mod size_asserts {
|
||||
|
||||
@ -40,15 +40,44 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||
| Range(_, Some(e), _)
|
||||
| Ret(Some(e))
|
||||
| Unary(_, e)
|
||||
| Yield(Some(e)) => {
|
||||
| Yield(Some(e))
|
||||
| Yeet(Some(e))
|
||||
| Become(e) => {
|
||||
expr = e;
|
||||
}
|
||||
Closure(closure) => {
|
||||
expr = &closure.body;
|
||||
}
|
||||
Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
|
||||
| TryBlock(..) | While(..) => break Some(expr),
|
||||
_ => break None,
|
||||
| TryBlock(..) | While(..) | ConstBlock(_) => break Some(expr),
|
||||
|
||||
// FIXME: These can end in `}`, but changing these would break stable code.
|
||||
InlineAsm(_) | OffsetOf(_, _) | MacCall(_) | IncludedBytes(_) | FormatArgs(_) => {
|
||||
break None;
|
||||
}
|
||||
|
||||
Break(_, None)
|
||||
| Range(_, None, _)
|
||||
| Ret(None)
|
||||
| Yield(None)
|
||||
| Array(_)
|
||||
| Call(_, _)
|
||||
| MethodCall(_)
|
||||
| Tup(_)
|
||||
| Lit(_)
|
||||
| Cast(_, _)
|
||||
| Type(_, _)
|
||||
| Await(_, _)
|
||||
| Field(_, _)
|
||||
| Index(_, _, _)
|
||||
| Underscore
|
||||
| Path(_, _)
|
||||
| Continue(_)
|
||||
| Repeat(_, _)
|
||||
| Paren(_)
|
||||
| Try(_)
|
||||
| Yeet(None)
|
||||
| Err => break None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +77,8 @@ impl LitKind {
|
||||
// new symbol because the string in the LitKind is different to the
|
||||
// string in the token.
|
||||
let s = symbol.as_str();
|
||||
// Vanilla strings are so common we optimize for the common case where no chars
|
||||
// requiring special behaviour are present.
|
||||
let symbol = if s.contains(['\\', '\r']) {
|
||||
let mut buf = String::with_capacity(s.len());
|
||||
let mut error = Ok(());
|
||||
@ -104,27 +106,20 @@ impl LitKind {
|
||||
LitKind::Str(symbol, ast::StrStyle::Cooked)
|
||||
}
|
||||
token::StrRaw(n) => {
|
||||
// Ditto.
|
||||
let s = symbol.as_str();
|
||||
let symbol =
|
||||
if s.contains('\r') {
|
||||
let mut buf = String::with_capacity(s.len());
|
||||
let mut error = Ok(());
|
||||
unescape_literal(s, Mode::RawStr, &mut |_, unescaped_char| {
|
||||
match unescaped_char {
|
||||
Ok(c) => buf.push(c),
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
}
|
||||
}
|
||||
// Raw strings have no escapes, so we only need to check for invalid chars, and we
|
||||
// can reuse the symbol on success.
|
||||
let mut error = Ok(());
|
||||
unescape_literal(symbol.as_str(), Mode::RawStr, &mut |_, unescaped_char| {
|
||||
match unescaped_char {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
}
|
||||
});
|
||||
error?;
|
||||
Symbol::intern(&buf)
|
||||
} else {
|
||||
symbol
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
error?;
|
||||
LitKind::Str(symbol, ast::StrStyle::Raw(n))
|
||||
}
|
||||
token::ByteStr => {
|
||||
@ -143,25 +138,19 @@ impl LitKind {
|
||||
LitKind::ByteStr(buf.into(), StrStyle::Cooked)
|
||||
}
|
||||
token::ByteStrRaw(n) => {
|
||||
// Raw strings have no escapes, so we only need to check for invalid chars, and we
|
||||
// can convert the symbol directly to a `Lrc<u8>` on success.
|
||||
let s = symbol.as_str();
|
||||
let bytes = if s.contains('\r') {
|
||||
let mut buf = Vec::with_capacity(s.len());
|
||||
let mut error = Ok(());
|
||||
unescape_literal(s, Mode::RawByteStr, &mut |_, c| match c {
|
||||
Ok(c) => buf.push(byte_from_char(c)),
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
}
|
||||
let mut error = Ok(());
|
||||
unescape_literal(s, Mode::RawByteStr, &mut |_, c| match c {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
}
|
||||
});
|
||||
error?;
|
||||
buf
|
||||
} else {
|
||||
symbol.to_string().into_bytes()
|
||||
};
|
||||
|
||||
LitKind::ByteStr(bytes.into(), StrStyle::Raw(n))
|
||||
}
|
||||
});
|
||||
LitKind::ByteStr(s.to_owned().into_bytes().into(), StrStyle::Raw(n))
|
||||
}
|
||||
token::CStr => {
|
||||
let s = symbol.as_str();
|
||||
@ -172,7 +161,6 @@ impl LitKind {
|
||||
error = Err(LitError::NulInCStr(span));
|
||||
}
|
||||
Ok(CStrUnit::Byte(b)) => buf.push(b),
|
||||
Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
|
||||
Ok(CStrUnit::Char(c)) => {
|
||||
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
|
||||
}
|
||||
@ -187,18 +175,15 @@ impl LitKind {
|
||||
LitKind::CStr(buf.into(), StrStyle::Cooked)
|
||||
}
|
||||
token::CStrRaw(n) => {
|
||||
// Raw strings have no escapes, so we only need to check for invalid chars, and we
|
||||
// can convert the symbol directly to a `Lrc<u8>` on success.
|
||||
let s = symbol.as_str();
|
||||
let mut buf = Vec::with_capacity(s.len());
|
||||
let mut error = Ok(());
|
||||
unescape_c_string(s, Mode::RawCStr, &mut |span, c| match c {
|
||||
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
|
||||
error = Err(LitError::NulInCStr(span));
|
||||
}
|
||||
Ok(CStrUnit::Byte(b)) => buf.push(b),
|
||||
Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
|
||||
Ok(CStrUnit::Char(c)) => {
|
||||
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
@ -206,6 +191,7 @@ impl LitKind {
|
||||
}
|
||||
});
|
||||
error?;
|
||||
let mut buf = s.to_owned().into_bytes();
|
||||
buf.push(0);
|
||||
LitKind::CStr(buf.into(), StrStyle::Raw(n))
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
//! instance, a walker looking for item names in a module will miss all of
|
||||
//! those that are created by the expansion of a macro.
|
||||
|
||||
use crate::{ast::*, StaticItem};
|
||||
use crate::ast::*;
|
||||
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::Span;
|
||||
@ -559,7 +559,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
|
||||
walk_list!(visitor, visit_expr, lower_bound);
|
||||
walk_list!(visitor, visit_expr, upper_bound);
|
||||
}
|
||||
PatKind::Wild | PatKind::Rest => {}
|
||||
PatKind::Wild | PatKind::Rest | PatKind::Never => {}
|
||||
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
|
||||
walk_list!(visitor, visit_pat, elems);
|
||||
}
|
||||
@ -861,7 +861,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
||||
ExprKind::Closure(box Closure {
|
||||
binder,
|
||||
capture_clause,
|
||||
asyncness: _,
|
||||
coroutine_kind: _,
|
||||
constness: _,
|
||||
movability: _,
|
||||
fn_decl,
|
||||
@ -951,7 +951,7 @@ pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) {
|
||||
pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
|
||||
visitor.visit_pat(&arm.pat);
|
||||
walk_list!(visitor, visit_expr, &arm.guard);
|
||||
visitor.visit_expr(&arm.body);
|
||||
walk_list!(visitor, visit_expr, &arm.body);
|
||||
walk_list!(visitor, visit_attribute, &arm.attrs);
|
||||
}
|
||||
|
||||
|
||||
@ -91,6 +91,10 @@ ast_lowering_invalid_register =
|
||||
ast_lowering_invalid_register_class =
|
||||
invalid register class `{$reg_class}`: {$error}
|
||||
|
||||
ast_lowering_match_arm_with_no_body =
|
||||
`match` arm with no body
|
||||
.suggestion = add a body after the pattern
|
||||
|
||||
ast_lowering_misplaced_assoc_ty_binding =
|
||||
associated type bounds are only allowed in where clauses and function signatures, not in {$position}
|
||||
|
||||
@ -104,6 +108,15 @@ ast_lowering_misplaced_impl_trait =
|
||||
ast_lowering_misplaced_relax_trait_bound =
|
||||
`?Trait` bounds are only permitted at the point where a type parameter is declared
|
||||
|
||||
ast_lowering_never_pattern_with_body =
|
||||
a never pattern is always unreachable
|
||||
.label = this will never be executed
|
||||
.suggestion = remove this expression
|
||||
|
||||
ast_lowering_never_pattern_with_guard =
|
||||
a guard on a never pattern will never be run
|
||||
.suggestion = remove this guard
|
||||
|
||||
ast_lowering_not_supported_for_lifetime_binder_async_closure =
|
||||
`for<...>` binders on `async` closures are not currently supported
|
||||
|
||||
|
||||
@ -14,8 +14,8 @@ use rustc_ast::*;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_target::asm;
|
||||
use std::collections::hash_map::Entry;
|
||||
@ -227,7 +227,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.create_def(
|
||||
parent_def_id.def_id,
|
||||
node_id,
|
||||
DefPathData::AnonConst,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
*op_sp,
|
||||
);
|
||||
let anon_const = AnonConst { id: node_id, value: P(expr) };
|
||||
@ -335,67 +336,81 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
hir::InlineAsmOperand::Const { .. }
|
||||
| hir::InlineAsmOperand::SymFn { .. }
|
||||
| hir::InlineAsmOperand::SymStatic { .. } => {
|
||||
unreachable!()
|
||||
unreachable!("{op:?} is not a register operand");
|
||||
}
|
||||
};
|
||||
|
||||
// Flag to output the error only once per operand
|
||||
let mut skip = false;
|
||||
reg.overlapping_regs(|r| {
|
||||
let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
|
||||
input| {
|
||||
match used_regs.entry(r) {
|
||||
Entry::Occupied(o) => {
|
||||
if skip {
|
||||
return;
|
||||
}
|
||||
skip = true;
|
||||
|
||||
let idx2 = *o.get();
|
||||
let (ref op2, op_sp2) = operands[idx2];
|
||||
let Some(asm::InlineAsmRegOrRegClass::Reg(reg2)) = op2.reg()
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
let in_out = match (op, op2) {
|
||||
(
|
||||
hir::InlineAsmOperand::In { .. },
|
||||
hir::InlineAsmOperand::Out { late, .. },
|
||||
)
|
||||
| (
|
||||
hir::InlineAsmOperand::Out { late, .. },
|
||||
hir::InlineAsmOperand::In { .. },
|
||||
) => {
|
||||
assert!(!*late);
|
||||
let out_op_sp = if input { op_sp2 } else { op_sp };
|
||||
Some(out_op_sp)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
sess.emit_err(RegisterConflict {
|
||||
op_span1: op_sp,
|
||||
op_span2: op_sp2,
|
||||
reg1_name: reg.name(),
|
||||
reg2_name: reg2.name(),
|
||||
in_out,
|
||||
});
|
||||
let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
|
||||
input,
|
||||
r: asm::InlineAsmReg| {
|
||||
match used_regs.entry(r) {
|
||||
Entry::Occupied(o) => {
|
||||
if skip {
|
||||
return;
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
if r == reg {
|
||||
v.insert(idx);
|
||||
skip = true;
|
||||
|
||||
let idx2 = *o.get();
|
||||
let (ref op2, op_sp2) = operands[idx2];
|
||||
|
||||
let in_out = match (op, op2) {
|
||||
(
|
||||
hir::InlineAsmOperand::In { .. },
|
||||
hir::InlineAsmOperand::Out { late, .. },
|
||||
)
|
||||
| (
|
||||
hir::InlineAsmOperand::Out { late, .. },
|
||||
hir::InlineAsmOperand::In { .. },
|
||||
) => {
|
||||
assert!(!*late);
|
||||
let out_op_sp = if input { op_sp2 } else { op_sp };
|
||||
Some(out_op_sp)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let reg_str = |idx| -> &str {
|
||||
// HIR asm doesn't preserve the original alias string of the explicit register,
|
||||
// so we have to retrieve it from AST
|
||||
let (op, _): &(InlineAsmOperand, Span) = &asm.operands[idx];
|
||||
if let Some(ast::InlineAsmRegOrRegClass::Reg(reg_sym)) =
|
||||
op.reg()
|
||||
{
|
||||
reg_sym.as_str()
|
||||
} else {
|
||||
unreachable!("{op:?} is not a register operand");
|
||||
}
|
||||
};
|
||||
|
||||
sess.emit_err(RegisterConflict {
|
||||
op_span1: op_sp,
|
||||
op_span2: op_sp2,
|
||||
reg1_name: reg_str(idx),
|
||||
reg2_name: reg_str(idx2),
|
||||
in_out,
|
||||
});
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
if r == reg {
|
||||
v.insert(idx);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
let mut overlapping_with = vec![];
|
||||
reg.overlapping_regs(|r| {
|
||||
overlapping_with.push(r);
|
||||
});
|
||||
for r in overlapping_with {
|
||||
if input {
|
||||
check(&mut used_input_regs, true);
|
||||
check(&mut used_input_regs, true, r);
|
||||
}
|
||||
if output {
|
||||
check(&mut used_output_regs, false);
|
||||
check(&mut used_output_regs, false, r);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -410,12 +425,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut output_used = false;
|
||||
let mut overlapping_with = vec![];
|
||||
clobber.overlapping_regs(|reg| {
|
||||
if used_output_regs.contains_key(®) {
|
||||
output_used = true;
|
||||
}
|
||||
overlapping_with.push(reg);
|
||||
});
|
||||
let output_used =
|
||||
overlapping_with.iter().any(|reg| used_output_regs.contains_key(®));
|
||||
|
||||
if !output_used {
|
||||
operands.push((
|
||||
|
||||
@ -340,6 +340,32 @@ pub struct NotSupportedForLifetimeBinderAsyncClosure {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_match_arm_with_no_body)]
|
||||
pub struct MatchArmWithNoBody {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
|
||||
pub suggestion: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_never_pattern_with_body)]
|
||||
pub struct NeverPatternWithBody {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
#[suggestion(code = "", applicability = "maybe-incorrect")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_never_pattern_with_guard)]
|
||||
pub struct NeverPatternWithGuard {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "", applicability = "maybe-incorrect")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic, Clone, Copy)]
|
||||
#[diag(ast_lowering_arbitrary_expression_in_pattern)]
|
||||
pub struct ArbitraryExpressionInPattern {
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
use super::errors::{
|
||||
AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
|
||||
BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
|
||||
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd,
|
||||
NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign,
|
||||
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
|
||||
NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
|
||||
UnderscoreExprLhsAssign,
|
||||
};
|
||||
use super::ResolverAstLoweringExt;
|
||||
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
|
||||
use crate::{FnDeclKind, ImplTraitPosition};
|
||||
use rustc_ast::attr;
|
||||
use rustc_ast::ptr::P as AstP;
|
||||
use rustc_ast::*;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_session::errors::report_lit_error;
|
||||
use rustc_span::source_map::{respan, Spanned};
|
||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_span::{DesugaringKind, Span};
|
||||
use thin_vec::{thin_vec, ThinVec};
|
||||
@ -42,8 +42,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
// Merge attributes into the inner expression.
|
||||
if !e.attrs.is_empty() {
|
||||
let old_attrs =
|
||||
self.attrs.get(&ex.hir_id.local_id).map(|la| *la).unwrap_or(&[]);
|
||||
let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]);
|
||||
self.attrs.insert(
|
||||
ex.hir_id.local_id,
|
||||
&*self.arena.alloc_from_iter(
|
||||
@ -73,7 +72,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let kind = match &e.kind {
|
||||
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
|
||||
ExprKind::ConstBlock(c) => {
|
||||
let c = self.with_new_scopes(|this| hir::ConstBlock {
|
||||
let c = self.with_new_scopes(c.value.span, |this| hir::ConstBlock {
|
||||
def_id: this.local_def_id(c.id),
|
||||
hir_id: this.lower_node_id(c.id),
|
||||
body: this.lower_const_body(c.value.span, Some(&c.value)),
|
||||
@ -190,46 +189,43 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
None,
|
||||
e.span,
|
||||
hir::CoroutineSource::Block,
|
||||
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
|
||||
|this| this.with_new_scopes(e.span, |this| this.lower_block_expr(block)),
|
||||
),
|
||||
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
|
||||
ExprKind::Closure(box Closure {
|
||||
binder,
|
||||
capture_clause,
|
||||
constness,
|
||||
asyncness,
|
||||
coroutine_kind,
|
||||
movability,
|
||||
fn_decl,
|
||||
body,
|
||||
fn_decl_span,
|
||||
fn_arg_span,
|
||||
}) => {
|
||||
if let Async::Yes { closure_id, .. } = asyncness {
|
||||
self.lower_expr_async_closure(
|
||||
binder,
|
||||
*capture_clause,
|
||||
e.id,
|
||||
hir_id,
|
||||
*closure_id,
|
||||
fn_decl,
|
||||
body,
|
||||
*fn_decl_span,
|
||||
*fn_arg_span,
|
||||
)
|
||||
} else {
|
||||
self.lower_expr_closure(
|
||||
binder,
|
||||
*capture_clause,
|
||||
e.id,
|
||||
*constness,
|
||||
*movability,
|
||||
fn_decl,
|
||||
body,
|
||||
*fn_decl_span,
|
||||
*fn_arg_span,
|
||||
)
|
||||
}
|
||||
}
|
||||
}) => match coroutine_kind {
|
||||
Some(coroutine_kind) => self.lower_expr_coroutine_closure(
|
||||
binder,
|
||||
*capture_clause,
|
||||
e.id,
|
||||
hir_id,
|
||||
*coroutine_kind,
|
||||
fn_decl,
|
||||
body,
|
||||
*fn_decl_span,
|
||||
*fn_arg_span,
|
||||
),
|
||||
None => self.lower_expr_closure(
|
||||
binder,
|
||||
*capture_clause,
|
||||
e.id,
|
||||
*constness,
|
||||
*movability,
|
||||
fn_decl,
|
||||
body,
|
||||
*fn_decl_span,
|
||||
*fn_arg_span,
|
||||
),
|
||||
},
|
||||
ExprKind::Block(blk, opt_label) => {
|
||||
let opt_label = self.lower_label(*opt_label);
|
||||
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
|
||||
@ -324,11 +320,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
None,
|
||||
e.span,
|
||||
hir::CoroutineSource::Block,
|
||||
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
|
||||
|this| this.with_new_scopes(e.span, |this| this.lower_block_expr(block)),
|
||||
),
|
||||
ExprKind::Gen(capture_clause, block, GenBlockKind::AsyncGen) => self
|
||||
.make_async_gen_expr(
|
||||
*capture_clause,
|
||||
e.id,
|
||||
None,
|
||||
e.span,
|
||||
hir::CoroutineSource::Block,
|
||||
|this| this.with_new_scopes(e.span, |this| this.lower_block_expr(block)),
|
||||
),
|
||||
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
|
||||
ExprKind::Err => hir::ExprKind::Err(
|
||||
self.tcx.sess.delay_span_bug(e.span, "lowered ExprKind::Err"),
|
||||
self.tcx.sess.span_delayed_bug(e.span, "lowered ExprKind::Err"),
|
||||
),
|
||||
ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr),
|
||||
|
||||
@ -351,30 +356,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_binop(&mut self, b: BinOp) -> hir::BinOp {
|
||||
Spanned {
|
||||
node: match b.node {
|
||||
BinOpKind::Add => hir::BinOpKind::Add,
|
||||
BinOpKind::Sub => hir::BinOpKind::Sub,
|
||||
BinOpKind::Mul => hir::BinOpKind::Mul,
|
||||
BinOpKind::Div => hir::BinOpKind::Div,
|
||||
BinOpKind::Rem => hir::BinOpKind::Rem,
|
||||
BinOpKind::And => hir::BinOpKind::And,
|
||||
BinOpKind::Or => hir::BinOpKind::Or,
|
||||
BinOpKind::BitXor => hir::BinOpKind::BitXor,
|
||||
BinOpKind::BitAnd => hir::BinOpKind::BitAnd,
|
||||
BinOpKind::BitOr => hir::BinOpKind::BitOr,
|
||||
BinOpKind::Shl => hir::BinOpKind::Shl,
|
||||
BinOpKind::Shr => hir::BinOpKind::Shr,
|
||||
BinOpKind::Eq => hir::BinOpKind::Eq,
|
||||
BinOpKind::Lt => hir::BinOpKind::Lt,
|
||||
BinOpKind::Le => hir::BinOpKind::Le,
|
||||
BinOpKind::Ne => hir::BinOpKind::Ne,
|
||||
BinOpKind::Ge => hir::BinOpKind::Ge,
|
||||
BinOpKind::Gt => hir::BinOpKind::Gt,
|
||||
},
|
||||
span: self.lower_span(b.span),
|
||||
}
|
||||
fn lower_binop(&mut self, b: BinOp) -> BinOp {
|
||||
Spanned { node: b.node, span: self.lower_span(b.span) }
|
||||
}
|
||||
|
||||
fn lower_legacy_const_generics(
|
||||
@ -396,7 +379,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let node_id = self.next_node_id();
|
||||
|
||||
// Add a definition for the in-band const def.
|
||||
self.create_def(parent_def_id.def_id, node_id, DefPathData::AnonConst, f.span);
|
||||
self.create_def(
|
||||
parent_def_id.def_id,
|
||||
node_id,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
f.span,
|
||||
);
|
||||
|
||||
let anon_const = AnonConst { id: node_id, value: arg };
|
||||
generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
|
||||
@ -525,7 +514,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
this.mark_span_with_reason(
|
||||
DesugaringKind::TryBlock,
|
||||
expr.span,
|
||||
this.allow_try_trait.clone(),
|
||||
Some(this.allow_try_trait.clone()),
|
||||
),
|
||||
expr,
|
||||
)
|
||||
@ -533,7 +522,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let try_span = this.mark_span_with_reason(
|
||||
DesugaringKind::TryBlock,
|
||||
this.tcx.sess.source_map().end_point(body.span),
|
||||
this.allow_try_trait.clone(),
|
||||
Some(this.allow_try_trait.clone()),
|
||||
);
|
||||
|
||||
(try_span, this.expr_unit(try_span))
|
||||
@ -561,13 +550,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
expr: &'hir hir::Expr<'hir>,
|
||||
overall_span: Span,
|
||||
) -> &'hir hir::Expr<'hir> {
|
||||
let constructor = self.arena.alloc(self.expr_lang_item_path(method_span, lang_item, None));
|
||||
let constructor = self.arena.alloc(self.expr_lang_item_path(method_span, lang_item));
|
||||
self.expr_call(overall_span, constructor, std::slice::from_ref(expr))
|
||||
}
|
||||
|
||||
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
||||
let pat = self.lower_pat(&arm.pat);
|
||||
let guard = arm.guard.as_ref().map(|cond| {
|
||||
let mut guard = arm.guard.as_ref().map(|cond| {
|
||||
if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
|
||||
hir::Guard::IfLet(self.arena.alloc(hir::Let {
|
||||
hir_id: self.next_id(),
|
||||
@ -582,14 +571,46 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
});
|
||||
let hir_id = self.next_id();
|
||||
let span = self.lower_span(arm.span);
|
||||
self.lower_attrs(hir_id, &arm.attrs);
|
||||
hir::Arm {
|
||||
hir_id,
|
||||
pat,
|
||||
guard,
|
||||
body: self.lower_expr(&arm.body),
|
||||
span: self.lower_span(arm.span),
|
||||
}
|
||||
let is_never_pattern = pat.is_never_pattern();
|
||||
let body = if let Some(body) = &arm.body
|
||||
&& !is_never_pattern
|
||||
{
|
||||
self.lower_expr(body)
|
||||
} else {
|
||||
// Either `body.is_none()` or `is_never_pattern` here.
|
||||
if !is_never_pattern {
|
||||
if self.tcx.features().never_patterns {
|
||||
// If the feature is off we already emitted the error after parsing.
|
||||
let suggestion = span.shrink_to_hi();
|
||||
self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
|
||||
}
|
||||
} else if let Some(body) = &arm.body {
|
||||
self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
|
||||
guard = None;
|
||||
} else if let Some(g) = &arm.guard {
|
||||
self.tcx.sess.emit_err(NeverPatternWithGuard { span: g.span });
|
||||
guard = None;
|
||||
}
|
||||
|
||||
// We add a fake `loop {}` arm body so that it typecks to `!`.
|
||||
// FIXME(never_patterns): Desugar into a call to `unreachable_unchecked`.
|
||||
let block = self.arena.alloc(hir::Block {
|
||||
stmts: &[],
|
||||
expr: None,
|
||||
hir_id: self.next_id(),
|
||||
rules: hir::BlockCheckMode::DefaultBlock,
|
||||
span,
|
||||
targeted_by_break: false,
|
||||
});
|
||||
self.arena.alloc(hir::Expr {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ExprKind::Loop(block, None, hir::LoopSource::Loop, span),
|
||||
span,
|
||||
})
|
||||
};
|
||||
hir::Arm { hir_id, pat, guard, body, span }
|
||||
}
|
||||
|
||||
/// Lower an `async` construct to a coroutine that implements `Future`.
|
||||
@ -613,9 +634,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span)));
|
||||
|
||||
// Resume argument type: `ResumeTy`
|
||||
let unstable_span =
|
||||
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
|
||||
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span, None);
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::Async,
|
||||
self.lower_span(span),
|
||||
Some(self.allow_gen_future.clone()),
|
||||
);
|
||||
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span);
|
||||
let input_ty = hir::Ty {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::TyKind::Path(resume_ty),
|
||||
@ -721,6 +745,87 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Lower a `async gen` construct to a generator that implements `AsyncIterator`.
|
||||
///
|
||||
/// This results in:
|
||||
///
|
||||
/// ```text
|
||||
/// static move? |_task_context| -> () {
|
||||
/// <body>
|
||||
/// }
|
||||
/// ```
|
||||
pub(super) fn make_async_gen_expr(
|
||||
&mut self,
|
||||
capture_clause: CaptureBy,
|
||||
closure_node_id: NodeId,
|
||||
_yield_ty: Option<hir::FnRetTy<'hir>>,
|
||||
span: Span,
|
||||
async_coroutine_source: hir::CoroutineSource,
|
||||
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
|
||||
) -> hir::ExprKind<'hir> {
|
||||
let output = hir::FnRetTy::DefaultReturn(self.lower_span(span));
|
||||
|
||||
// Resume argument type: `ResumeTy`
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::Async,
|
||||
self.lower_span(span),
|
||||
Some(self.allow_gen_future.clone()),
|
||||
);
|
||||
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span);
|
||||
let input_ty = hir::Ty {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::TyKind::Path(resume_ty),
|
||||
span: unstable_span,
|
||||
};
|
||||
|
||||
// The closure/coroutine `FnDecl` takes a single (resume) argument of type `input_ty`.
|
||||
let fn_decl = self.arena.alloc(hir::FnDecl {
|
||||
inputs: arena_vec![self; input_ty],
|
||||
output,
|
||||
c_variadic: false,
|
||||
implicit_self: hir::ImplicitSelfKind::None,
|
||||
lifetime_elision_allowed: false,
|
||||
});
|
||||
|
||||
// Lower the argument pattern/ident. The ident is used again in the `.await` lowering.
|
||||
let (pat, task_context_hid) = self.pat_ident_binding_mode(
|
||||
span,
|
||||
Ident::with_dummy_span(sym::_task_context),
|
||||
hir::BindingAnnotation::MUT,
|
||||
);
|
||||
let param = hir::Param {
|
||||
hir_id: self.next_id(),
|
||||
pat,
|
||||
ty_span: self.lower_span(span),
|
||||
span: self.lower_span(span),
|
||||
};
|
||||
let params = arena_vec![self; param];
|
||||
|
||||
let body = self.lower_body(move |this| {
|
||||
this.coroutine_kind = Some(hir::CoroutineKind::AsyncGen(async_coroutine_source));
|
||||
|
||||
let old_ctx = this.task_context;
|
||||
this.task_context = Some(task_context_hid);
|
||||
let res = body(this);
|
||||
this.task_context = old_ctx;
|
||||
(params, res)
|
||||
});
|
||||
|
||||
// `static |_task_context| -> <ret_ty> { body }`:
|
||||
hir::ExprKind::Closure(self.arena.alloc(hir::Closure {
|
||||
def_id: self.local_def_id(closure_node_id),
|
||||
binder: hir::ClosureBinder::Default,
|
||||
capture_clause,
|
||||
bound_generic_params: &[],
|
||||
fn_decl,
|
||||
body,
|
||||
fn_decl_span: self.lower_span(span),
|
||||
fn_arg_span: None,
|
||||
movability: Some(hir::Movability::Static),
|
||||
constness: hir::Constness::NotConst,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to
|
||||
/// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled.
|
||||
pub(super) fn maybe_forward_track_caller(
|
||||
@ -736,7 +841,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::Async,
|
||||
span,
|
||||
self.allow_gen_future.clone(),
|
||||
Some(self.allow_gen_future.clone()),
|
||||
);
|
||||
self.lower_attrs(
|
||||
inner_hir_id,
|
||||
@ -770,20 +875,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
/// ```
|
||||
fn lower_expr_await(&mut self, await_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
|
||||
let full_span = expr.span.to(await_kw_span);
|
||||
match self.coroutine_kind {
|
||||
Some(hir::CoroutineKind::Async(_)) => {}
|
||||
|
||||
let is_async_gen = match self.coroutine_kind {
|
||||
Some(hir::CoroutineKind::Async(_)) => false,
|
||||
Some(hir::CoroutineKind::AsyncGen(_)) => true,
|
||||
Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) | None => {
|
||||
self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks {
|
||||
return hir::ExprKind::Err(self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks {
|
||||
await_kw_span,
|
||||
item_span: self.current_item,
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, None);
|
||||
let gen_future_span = self.mark_span_with_reason(
|
||||
DesugaringKind::Await,
|
||||
full_span,
|
||||
self.allow_gen_future.clone(),
|
||||
Some(self.allow_gen_future.clone()),
|
||||
);
|
||||
let expr = self.lower_expr_mut(expr);
|
||||
let expr_hir_id = expr.hir_id;
|
||||
@ -792,8 +900,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
// debuggers and debugger extensions expect it to be called `__awaitee`. They use
|
||||
// this name to identify what is being awaited by a suspended async functions.
|
||||
let awaitee_ident = Ident::with_dummy_span(sym::__awaitee);
|
||||
let (awaitee_pat, awaitee_pat_hid) =
|
||||
self.pat_ident_binding_mode(span, awaitee_ident, hir::BindingAnnotation::MUT);
|
||||
let (awaitee_pat, awaitee_pat_hid) = self.pat_ident_binding_mode(
|
||||
gen_future_span,
|
||||
awaitee_ident,
|
||||
hir::BindingAnnotation::MUT,
|
||||
);
|
||||
|
||||
let task_context_ident = Ident::with_dummy_span(sym::_task_context);
|
||||
|
||||
@ -806,29 +917,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let poll_expr = {
|
||||
let awaitee = self.expr_ident(span, awaitee_ident, awaitee_pat_hid);
|
||||
let ref_mut_awaitee = self.expr_mut_addr_of(span, awaitee);
|
||||
let task_context = if let Some(task_context_hid) = self.task_context {
|
||||
self.expr_ident_mut(span, task_context_ident, task_context_hid)
|
||||
} else {
|
||||
// Use of `await` outside of an async context, we cannot use `task_context` here.
|
||||
self.expr_err(span, self.tcx.sess.delay_span_bug(span, "no task_context hir id"))
|
||||
|
||||
let Some(task_context_hid) = self.task_context else {
|
||||
unreachable!("use of `await` outside of an async context.");
|
||||
};
|
||||
|
||||
let task_context = self.expr_ident_mut(span, task_context_ident, task_context_hid);
|
||||
|
||||
let new_unchecked = self.expr_call_lang_item_fn_mut(
|
||||
span,
|
||||
hir::LangItem::PinNewUnchecked,
|
||||
arena_vec![self; ref_mut_awaitee],
|
||||
Some(expr_hir_id),
|
||||
);
|
||||
let get_context = self.expr_call_lang_item_fn_mut(
|
||||
gen_future_span,
|
||||
hir::LangItem::GetContext,
|
||||
arena_vec![self; task_context],
|
||||
Some(expr_hir_id),
|
||||
);
|
||||
let call = self.expr_call_lang_item_fn(
|
||||
span,
|
||||
hir::LangItem::FuturePoll,
|
||||
arena_vec![self; new_unchecked, get_context],
|
||||
Some(expr_hir_id),
|
||||
);
|
||||
self.arena.alloc(self.expr_unsafe(call))
|
||||
};
|
||||
@ -841,12 +950,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let (x_pat, x_pat_hid) = self.pat_ident(gen_future_span, x_ident);
|
||||
let x_expr = self.expr_ident(gen_future_span, x_ident, x_pat_hid);
|
||||
let ready_field = self.single_pat_field(gen_future_span, x_pat);
|
||||
let ready_pat = self.pat_lang_item_variant(
|
||||
span,
|
||||
hir::LangItem::PollReady,
|
||||
ready_field,
|
||||
Some(expr_hir_id),
|
||||
);
|
||||
let ready_pat = self.pat_lang_item_variant(span, hir::LangItem::PollReady, ready_field);
|
||||
let break_x = self.with_loop_scope(loop_node_id, move |this| {
|
||||
let expr_break =
|
||||
hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
|
||||
@ -857,12 +961,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
|
||||
// `::std::task::Poll::Pending => {}`
|
||||
let pending_arm = {
|
||||
let pending_pat = self.pat_lang_item_variant(
|
||||
span,
|
||||
hir::LangItem::PollPending,
|
||||
&[],
|
||||
Some(expr_hir_id),
|
||||
);
|
||||
let pending_pat = self.pat_lang_item_variant(span, hir::LangItem::PollPending, &[]);
|
||||
let empty_block = self.expr_block_empty(span);
|
||||
self.arm(pending_pat, empty_block)
|
||||
};
|
||||
@ -877,25 +976,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
self.stmt_expr(span, match_expr)
|
||||
};
|
||||
|
||||
// task_context = yield ();
|
||||
// Depending on `async` of `async gen`:
|
||||
// async - task_context = yield ();
|
||||
// async gen - task_context = yield ASYNC_GEN_PENDING;
|
||||
let yield_stmt = {
|
||||
let unit = self.expr_unit(span);
|
||||
let yielded = if is_async_gen {
|
||||
self.arena.alloc(self.expr_lang_item_path(span, hir::LangItem::AsyncGenPending))
|
||||
} else {
|
||||
self.expr_unit(span)
|
||||
};
|
||||
|
||||
let yield_expr = self.expr(
|
||||
span,
|
||||
hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr_hir_id) }),
|
||||
hir::ExprKind::Yield(yielded, hir::YieldSource::Await { expr: Some(expr_hir_id) }),
|
||||
);
|
||||
let yield_expr = self.arena.alloc(yield_expr);
|
||||
|
||||
if let Some(task_context_hid) = self.task_context {
|
||||
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
|
||||
let assign =
|
||||
self.expr(span, hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)));
|
||||
self.stmt_expr(span, assign)
|
||||
} else {
|
||||
// Use of `await` outside of an async context. Return `yield_expr` so that we can
|
||||
// proceed with type checking.
|
||||
self.stmt(span, hir::StmtKind::Semi(yield_expr))
|
||||
}
|
||||
let Some(task_context_hid) = self.task_context else {
|
||||
unreachable!("use of `await` outside of an async context.");
|
||||
};
|
||||
|
||||
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
|
||||
let assign =
|
||||
self.expr(span, hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)));
|
||||
self.stmt_expr(span, assign)
|
||||
};
|
||||
|
||||
let loop_block = self.block_all(span, arena_vec![self; inner_match_stmt, yield_stmt], None);
|
||||
@ -920,7 +1024,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
span,
|
||||
hir::LangItem::IntoFutureIntoFuture,
|
||||
arena_vec![self; expr],
|
||||
Some(expr_hir_id),
|
||||
);
|
||||
|
||||
// match <into_future_expr> {
|
||||
@ -947,9 +1050,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
) -> hir::ExprKind<'hir> {
|
||||
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
|
||||
|
||||
let (body_id, coroutine_option) = self.with_new_scopes(move |this| {
|
||||
let prev = this.current_item;
|
||||
this.current_item = Some(fn_decl_span);
|
||||
let (body_id, coroutine_option) = self.with_new_scopes(fn_decl_span, move |this| {
|
||||
let mut coroutine_kind = None;
|
||||
let body_id = this.lower_fn_body(decl, |this| {
|
||||
let e = this.lower_expr_mut(body);
|
||||
@ -957,8 +1058,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
e
|
||||
});
|
||||
let coroutine_option =
|
||||
this.coroutine_movability_for_fn(&decl, fn_decl_span, coroutine_kind, movability);
|
||||
this.current_item = prev;
|
||||
this.coroutine_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability);
|
||||
(body_id, coroutine_option)
|
||||
});
|
||||
|
||||
@ -996,7 +1096,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
Some(movability)
|
||||
}
|
||||
Some(hir::CoroutineKind::Gen(_)) | Some(hir::CoroutineKind::Async(_)) => {
|
||||
Some(
|
||||
hir::CoroutineKind::Gen(_)
|
||||
| hir::CoroutineKind::Async(_)
|
||||
| hir::CoroutineKind::AsyncGen(_),
|
||||
) => {
|
||||
panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering");
|
||||
}
|
||||
None => {
|
||||
@ -1023,18 +1127,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
(binder, params)
|
||||
}
|
||||
|
||||
fn lower_expr_async_closure(
|
||||
fn lower_expr_coroutine_closure(
|
||||
&mut self,
|
||||
binder: &ClosureBinder,
|
||||
capture_clause: CaptureBy,
|
||||
closure_id: NodeId,
|
||||
closure_hir_id: hir::HirId,
|
||||
inner_closure_id: NodeId,
|
||||
coroutine_kind: CoroutineKind,
|
||||
decl: &FnDecl,
|
||||
body: &Expr,
|
||||
fn_decl_span: Span,
|
||||
fn_arg_span: Span,
|
||||
) -> hir::ExprKind<'hir> {
|
||||
let CoroutineKind::Async { closure_id: inner_closure_id, .. } = coroutine_kind else {
|
||||
span_bug!(fn_decl_span, "`async gen` and `gen` closures are not supported, yet");
|
||||
};
|
||||
|
||||
if let &ClosureBinder::For { span, .. } = binder {
|
||||
self.tcx.sess.emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
|
||||
}
|
||||
@ -1044,7 +1152,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let outer_decl =
|
||||
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
|
||||
|
||||
let body = self.with_new_scopes(|this| {
|
||||
let body = self.with_new_scopes(fn_decl_span, |this| {
|
||||
// FIXME(cramertj): allow `async` non-`move` closures with arguments.
|
||||
if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() {
|
||||
this.tcx.sess.emit_err(AsyncNonMoveClosureNotSupported { fn_decl_span });
|
||||
@ -1055,7 +1163,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let body_id = this.lower_fn_body(&outer_decl, |this| {
|
||||
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
|
||||
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);
|
||||
Some(hir::FnRetTy::Return(this.lower_ty(&ty, &itctx)))
|
||||
Some(hir::FnRetTy::Return(this.lower_ty(ty, &itctx)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -1066,7 +1174,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
async_ret_ty,
|
||||
body.span,
|
||||
hir::CoroutineSource::Closure,
|
||||
|this| this.with_new_scopes(|this| this.lower_expr_mut(body)),
|
||||
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
|
||||
);
|
||||
let hir_id = this.lower_node_id(inner_closure_id);
|
||||
this.maybe_forward_track_caller(body.span, closure_hir_id, hir_id);
|
||||
@ -1113,6 +1221,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
| ExprKind::Struct(..)
|
||||
| ExprKind::Tup(..)
|
||||
| ExprKind::Underscore => false,
|
||||
// Check for unit struct constructor.
|
||||
ExprKind::Path(..) => lower_ctx.extract_unit_struct_path(lhs).is_none(),
|
||||
// Check for tuple struct constructor.
|
||||
ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
|
||||
ExprKind::Paren(e) => {
|
||||
@ -1149,12 +1259,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
);
|
||||
|
||||
// `a = lhs1; b = lhs2;`.
|
||||
let stmts = self
|
||||
.arena
|
||||
.alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter()));
|
||||
let stmts = self.arena.alloc_from_iter(std::iter::once(destructure_let).chain(assignments));
|
||||
|
||||
// Wrap everything in a block.
|
||||
hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None)
|
||||
hir::ExprKind::Block(self.block_all(whole_span, stmts, None), None)
|
||||
}
|
||||
|
||||
/// If the given expression is a path to a tuple struct, returns that path.
|
||||
@ -1377,8 +1485,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
|
||||
let e1 = self.lower_expr_mut(e1);
|
||||
let e2 = self.lower_expr_mut(e2);
|
||||
let fn_path =
|
||||
hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, self.lower_span(span), None);
|
||||
let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, self.lower_span(span));
|
||||
let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path)));
|
||||
hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
|
||||
}
|
||||
@ -1411,7 +1518,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let fields = self.arena.alloc_from_iter(
|
||||
e1.iter().map(|e| (sym::start, e)).chain(e2.iter().map(|e| (sym::end, e))).map(
|
||||
|(s, e)| {
|
||||
let expr = self.lower_expr(&e);
|
||||
let expr = self.lower_expr(e);
|
||||
let ident = Ident::new(s, self.lower_span(e.span));
|
||||
self.expr_field(ident, expr, e.span)
|
||||
},
|
||||
@ -1419,7 +1526,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
);
|
||||
|
||||
hir::ExprKind::Struct(
|
||||
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span), None)),
|
||||
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
|
||||
fields,
|
||||
None,
|
||||
)
|
||||
@ -1504,10 +1611,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
|
||||
fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
|
||||
match self.coroutine_kind {
|
||||
Some(hir::CoroutineKind::Gen(_)) => {}
|
||||
let is_async_gen = match self.coroutine_kind {
|
||||
Some(hir::CoroutineKind::Gen(_)) => false,
|
||||
Some(hir::CoroutineKind::AsyncGen(_)) => true,
|
||||
Some(hir::CoroutineKind::Async(_)) => {
|
||||
self.tcx.sess.emit_err(AsyncCoroutinesNotSupported { span });
|
||||
return hir::ExprKind::Err(
|
||||
self.tcx.sess.emit_err(AsyncCoroutinesNotSupported { span }),
|
||||
);
|
||||
}
|
||||
Some(hir::CoroutineKind::Coroutine) | None => {
|
||||
if !self.tcx.features().coroutines {
|
||||
@ -1519,14 +1629,37 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine)
|
||||
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine);
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let expr =
|
||||
let yielded =
|
||||
opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
|
||||
|
||||
hir::ExprKind::Yield(expr, hir::YieldSource::Yield)
|
||||
if is_async_gen {
|
||||
// `yield $expr` is transformed into `task_context = yield async_gen_ready($expr)`.
|
||||
// This ensures that we store our resumed `ResumeContext` correctly, and also that
|
||||
// the apparent value of the `yield` expression is `()`.
|
||||
let wrapped_yielded = self.expr_call_lang_item_fn(
|
||||
span,
|
||||
hir::LangItem::AsyncGenReady,
|
||||
std::slice::from_ref(yielded),
|
||||
);
|
||||
let yield_expr = self.arena.alloc(
|
||||
self.expr(span, hir::ExprKind::Yield(wrapped_yielded, hir::YieldSource::Yield)),
|
||||
);
|
||||
|
||||
let Some(task_context_hid) = self.task_context else {
|
||||
unreachable!("use of `await` outside of an async context.");
|
||||
};
|
||||
let task_context_ident = Ident::with_dummy_span(sym::_task_context);
|
||||
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
|
||||
|
||||
hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span))
|
||||
} else {
|
||||
hir::ExprKind::Yield(yielded, hir::YieldSource::Yield)
|
||||
}
|
||||
}
|
||||
|
||||
/// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into:
|
||||
@ -1588,7 +1721,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
head_span,
|
||||
hir::LangItem::IteratorNext,
|
||||
arena_vec![self; ref_mut_iter],
|
||||
None,
|
||||
);
|
||||
let arms = arena_vec![self; none_arm, some_arm];
|
||||
|
||||
@ -1617,7 +1749,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
head_span,
|
||||
hir::LangItem::IntoIterIntoIter,
|
||||
arena_vec![self; head],
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
@ -1655,13 +1786,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::QuestionMark,
|
||||
span,
|
||||
self.allow_try_trait.clone(),
|
||||
Some(self.allow_try_trait.clone()),
|
||||
);
|
||||
let try_span = self.tcx.sess.source_map().end_point(span);
|
||||
let try_span = self.mark_span_with_reason(
|
||||
DesugaringKind::QuestionMark,
|
||||
try_span,
|
||||
self.allow_try_trait.clone(),
|
||||
Some(self.allow_try_trait.clone()),
|
||||
);
|
||||
|
||||
// `Try::branch(<expr>)`
|
||||
@ -1673,7 +1804,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
unstable_span,
|
||||
hir::LangItem::TryTraitBranch,
|
||||
arena_vec![self; sub_expr],
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
@ -1755,7 +1885,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::YeetExpr,
|
||||
span,
|
||||
self.allow_try_trait.clone(),
|
||||
Some(self.allow_try_trait.clone()),
|
||||
);
|
||||
|
||||
let from_yeet_expr = self.wrap_in_try_constructor(
|
||||
@ -1878,9 +2008,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
span: Span,
|
||||
lang_item: hir::LangItem,
|
||||
args: &'hir [hir::Expr<'hir>],
|
||||
hir_id: Option<hir::HirId>,
|
||||
) -> hir::Expr<'hir> {
|
||||
let path = self.arena.alloc(self.expr_lang_item_path(span, lang_item, hir_id));
|
||||
let path = self.arena.alloc(self.expr_lang_item_path(span, lang_item));
|
||||
self.expr_call_mut(span, path, args)
|
||||
}
|
||||
|
||||
@ -1889,21 +2018,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
span: Span,
|
||||
lang_item: hir::LangItem,
|
||||
args: &'hir [hir::Expr<'hir>],
|
||||
hir_id: Option<hir::HirId>,
|
||||
) -> &'hir hir::Expr<'hir> {
|
||||
self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args, hir_id))
|
||||
self.arena.alloc(self.expr_call_lang_item_fn_mut(span, lang_item, args))
|
||||
}
|
||||
|
||||
fn expr_lang_item_path(
|
||||
&mut self,
|
||||
span: Span,
|
||||
lang_item: hir::LangItem,
|
||||
hir_id: Option<hir::HirId>,
|
||||
) -> hir::Expr<'hir> {
|
||||
self.expr(
|
||||
span,
|
||||
hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span), hir_id)),
|
||||
)
|
||||
fn expr_lang_item_path(&mut self, span: Span, lang_item: hir::LangItem) -> hir::Expr<'hir> {
|
||||
self.expr(span, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span))))
|
||||
}
|
||||
|
||||
/// `<LangItem>::name`
|
||||
@ -1916,7 +2036,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let path = hir::ExprKind::Path(hir::QPath::TypeRelative(
|
||||
self.arena.alloc(self.ty(
|
||||
span,
|
||||
hir::TyKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span), None)),
|
||||
hir::TyKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span))),
|
||||
)),
|
||||
self.arena.alloc(hir::PathSegment::new(
|
||||
Ident::new(name, span),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use super::LoweringContext;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::visit::{self, Visitor};
|
||||
use rustc_ast::visit::Visitor;
|
||||
use rustc_ast::*;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir as hir;
|
||||
@ -267,7 +267,7 @@ fn make_count<'hir>(
|
||||
ctx.expr(
|
||||
sp,
|
||||
hir::ExprKind::Err(
|
||||
ctx.tcx.sess.delay_span_bug(sp, "lowered bad format_args count"),
|
||||
ctx.tcx.sess.span_delayed_bug(sp, "lowered bad format_args count"),
|
||||
),
|
||||
)
|
||||
}
|
||||
@ -306,7 +306,7 @@ fn make_format_spec<'hir>(
|
||||
}
|
||||
Err(_) => ctx.expr(
|
||||
sp,
|
||||
hir::ExprKind::Err(ctx.tcx.sess.delay_span_bug(sp, "lowered bad format_args count")),
|
||||
hir::ExprKind::Err(ctx.tcx.sess.span_delayed_bug(sp, "lowered bad format_args count")),
|
||||
),
|
||||
};
|
||||
let &FormatOptions {
|
||||
@ -338,8 +338,8 @@ fn make_format_spec<'hir>(
|
||||
| ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
|
||||
| ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
|
||||
let flags = ctx.expr_u32(sp, flags);
|
||||
let precision = make_count(ctx, sp, &precision, argmap);
|
||||
let width = make_count(ctx, sp, &width, argmap);
|
||||
let precision = make_count(ctx, sp, precision, argmap);
|
||||
let width = make_count(ctx, sp, width, argmap);
|
||||
let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||
sp,
|
||||
hir::LangItem::FormatPlaceholder,
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::*;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::span_bug;
|
||||
@ -10,14 +9,14 @@ use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
/// A visitor that walks over the HIR and collects `Node`s into a HIR map.
|
||||
pub(super) struct NodeCollector<'a, 'hir> {
|
||||
struct NodeCollector<'a, 'hir> {
|
||||
tcx: TyCtxt<'hir>,
|
||||
|
||||
bodies: &'a SortedMap<ItemLocalId, &'hir Body<'hir>>,
|
||||
|
||||
/// Outputs
|
||||
nodes: IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>,
|
||||
parenting: FxHashMap<LocalDefId, ItemLocalId>,
|
||||
parenting: LocalDefIdMap<ItemLocalId>,
|
||||
|
||||
/// The parent of this node
|
||||
parent_node: hir::ItemLocalId,
|
||||
@ -30,7 +29,7 @@ pub(super) fn index_hir<'hir>(
|
||||
tcx: TyCtxt<'hir>,
|
||||
item: hir::OwnerNode<'hir>,
|
||||
bodies: &SortedMap<ItemLocalId, &'hir Body<'hir>>,
|
||||
) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, FxHashMap<LocalDefId, ItemLocalId>) {
|
||||
) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, LocalDefIdMap<ItemLocalId>) {
|
||||
let mut nodes = IndexVec::new();
|
||||
// This node's parent should never be accessed: the owner's parent is computed by the
|
||||
// hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is
|
||||
@ -42,12 +41,12 @@ pub(super) fn index_hir<'hir>(
|
||||
parent_node: ItemLocalId::new(0),
|
||||
nodes,
|
||||
bodies,
|
||||
parenting: FxHashMap::default(),
|
||||
parenting: Default::default(),
|
||||
};
|
||||
|
||||
match item {
|
||||
OwnerNode::Crate(citem) => {
|
||||
collector.visit_mod(&citem, citem.spans.inner_span, hir::CRATE_HIR_ID)
|
||||
collector.visit_mod(citem, citem.spans.inner_span, hir::CRATE_HIR_ID)
|
||||
}
|
||||
OwnerNode::Item(item) => collector.visit_item(item),
|
||||
OwnerNode::TraitItem(item) => collector.visit_trait_item(item),
|
||||
@ -89,7 +88,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
self.nodes.insert(hir_id.local_id, ParentedNode { parent: self.parent_node, node: node });
|
||||
self.nodes.insert(hir_id.local_id, ParentedNode { parent: self.parent_node, node });
|
||||
}
|
||||
|
||||
fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_node_id: HirId, f: F) {
|
||||
|
||||
@ -3,11 +3,9 @@ use super::ResolverAstLoweringExt;
|
||||
use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
|
||||
use super::{FnDeclKind, LoweringContext, ParamMode};
|
||||
|
||||
use hir::definitions::DefPathData;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::AssocCtxt;
|
||||
use rustc_ast::*;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
@ -55,42 +53,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
||||
owner: NodeId,
|
||||
f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>,
|
||||
) {
|
||||
let allow_gen_future = Some(if self.tcx.features().async_fn_track_caller {
|
||||
[sym::gen_future, sym::closure_track_caller][..].into()
|
||||
} else {
|
||||
[sym::gen_future][..].into()
|
||||
});
|
||||
let mut lctx = LoweringContext {
|
||||
// Pseudo-globals.
|
||||
tcx: self.tcx,
|
||||
resolver: self.resolver,
|
||||
arena: self.tcx.hir_arena,
|
||||
|
||||
// HirId handling.
|
||||
bodies: Vec::new(),
|
||||
attrs: SortedMap::default(),
|
||||
children: Vec::default(),
|
||||
current_hir_id_owner: hir::CRATE_OWNER_ID,
|
||||
item_local_id_counter: hir::ItemLocalId::new(0),
|
||||
node_id_to_local_id: Default::default(),
|
||||
trait_map: Default::default(),
|
||||
|
||||
// Lowering state.
|
||||
catch_scope: None,
|
||||
loop_scope: None,
|
||||
is_in_loop_condition: false,
|
||||
is_in_trait_impl: false,
|
||||
is_in_dyn_type: false,
|
||||
coroutine_kind: None,
|
||||
task_context: None,
|
||||
current_item: None,
|
||||
impl_trait_defs: Vec::new(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
|
||||
allow_gen_future,
|
||||
generics_def_id_map: Default::default(),
|
||||
host_param_id: None,
|
||||
};
|
||||
let mut lctx = LoweringContext::new(self.tcx, self.resolver);
|
||||
lctx.with_hir_id_owner(owner, |lctx| f(lctx));
|
||||
|
||||
for (def_id, info) in lctx.children {
|
||||
@ -136,39 +99,9 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
||||
|
||||
fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) {
|
||||
let def_id = self.resolver.node_id_to_def_id[&item.id];
|
||||
|
||||
let parent_id = self.tcx.local_parent(def_id);
|
||||
let parent_hir = self.lower_node(parent_id).unwrap();
|
||||
self.with_lctx(item.id, |lctx| {
|
||||
// Evaluate with the lifetimes in `params` in-scope.
|
||||
// This is used to track which lifetimes have already been defined,
|
||||
// and which need to be replicated when lowering an async fn.
|
||||
|
||||
match parent_hir.node().expect_item().kind {
|
||||
hir::ItemKind::Impl(impl_) => {
|
||||
lctx.is_in_trait_impl = impl_.of_trait.is_some();
|
||||
}
|
||||
hir::ItemKind::Trait(_, _, generics, _, _) if lctx.tcx.features().effects => {
|
||||
lctx.host_param_id = generics
|
||||
.params
|
||||
.iter()
|
||||
.find(|param| {
|
||||
parent_hir
|
||||
.attrs
|
||||
.get(param.hir_id.local_id)
|
||||
.iter()
|
||||
.any(|attr| attr.has_name(sym::rustc_host))
|
||||
})
|
||||
.map(|param| param.def_id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match ctxt {
|
||||
AssocCtxt::Trait => hir::OwnerNode::TraitItem(lctx.lower_trait_item(item)),
|
||||
AssocCtxt::Impl => hir::OwnerNode::ImplItem(lctx.lower_impl_item(item)),
|
||||
}
|
||||
})
|
||||
self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt, parent_hir))
|
||||
}
|
||||
|
||||
fn lower_foreign_item(&mut self, item: &ForeignItem) {
|
||||
@ -268,27 +201,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
body,
|
||||
..
|
||||
}) => {
|
||||
self.with_new_scopes(|this| {
|
||||
this.current_item = Some(ident.span);
|
||||
|
||||
self.with_new_scopes(ident.span, |this| {
|
||||
// Note: we don't need to change the return type from `T` to
|
||||
// `impl Future<Output = T>` here because lower_body
|
||||
// only cares about the input argument patterns in the function
|
||||
// declaration (decl), not the return types.
|
||||
let asyncness = header.asyncness;
|
||||
let body_id = this.lower_maybe_async_body(
|
||||
let coroutine_kind = header.coroutine_kind;
|
||||
let body_id = this.lower_maybe_coroutine_body(
|
||||
span,
|
||||
hir_id,
|
||||
&decl,
|
||||
asyncness,
|
||||
decl,
|
||||
coroutine_kind,
|
||||
body.as_deref(),
|
||||
);
|
||||
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, decl) =
|
||||
this.lower_generics(generics, header.constness, id, &itctx, |this| {
|
||||
let ret_id = asyncness.opt_return_id();
|
||||
this.lower_fn_decl(&decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id)
|
||||
this.lower_fn_decl(
|
||||
decl,
|
||||
id,
|
||||
*fn_sig_span,
|
||||
FnDeclKind::Fn,
|
||||
coroutine_kind,
|
||||
)
|
||||
});
|
||||
let sig = hir::FnSig {
|
||||
decl,
|
||||
@ -329,7 +265,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| match ty {
|
||||
None => {
|
||||
let guar = this.tcx.sess.delay_span_bug(
|
||||
let guar = this.tcx.sess.span_delayed_bug(
|
||||
span,
|
||||
"expected to lower type alias type, but it was missing",
|
||||
);
|
||||
@ -485,7 +421,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
ItemKind::MacroDef(MacroDef { body, macro_rules }) => {
|
||||
let body = P(self.lower_delim_args(body));
|
||||
let macro_kind = self.resolver.decl_macro_kind(self.local_def_id(id));
|
||||
let def_id = self.local_def_id(id);
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
let DefKind::Macro(macro_kind) = def_kind else {
|
||||
unreachable!(
|
||||
"expected DefKind::Macro for macro item, found {}",
|
||||
def_kind.descr(def_id.to_def_id())
|
||||
);
|
||||
};
|
||||
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
|
||||
hir::ItemKind::Macro(macro_def, macro_kind)
|
||||
}
|
||||
@ -614,6 +557,41 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_assoc_item(
|
||||
&mut self,
|
||||
item: &AssocItem,
|
||||
ctxt: AssocCtxt,
|
||||
parent_hir: &'hir hir::OwnerInfo<'hir>,
|
||||
) -> hir::OwnerNode<'hir> {
|
||||
// Evaluate with the lifetimes in `params` in-scope.
|
||||
// This is used to track which lifetimes have already been defined,
|
||||
// and which need to be replicated when lowering an async fn.
|
||||
|
||||
match parent_hir.node().expect_item().kind {
|
||||
hir::ItemKind::Impl(impl_) => {
|
||||
self.is_in_trait_impl = impl_.of_trait.is_some();
|
||||
}
|
||||
hir::ItemKind::Trait(_, _, generics, _, _) if self.tcx.features().effects => {
|
||||
self.host_param_id = generics
|
||||
.params
|
||||
.iter()
|
||||
.find(|param| {
|
||||
matches!(
|
||||
param.kind,
|
||||
hir::GenericParamKind::Const { is_host_effect: true, .. }
|
||||
)
|
||||
})
|
||||
.map(|param| param.def_id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match ctxt {
|
||||
AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)),
|
||||
AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
|
||||
let hir_id = self.lower_node_id(i.id);
|
||||
let owner_id = hir_id.expect_owner();
|
||||
@ -683,11 +661,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
vdata: &VariantData,
|
||||
) -> hir::VariantData<'hir> {
|
||||
match vdata {
|
||||
VariantData::Struct(fields, recovered) => hir::VariantData::Struct(
|
||||
self.arena
|
||||
VariantData::Struct { fields, recovered } => hir::VariantData::Struct {
|
||||
fields: self
|
||||
.arena
|
||||
.alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))),
|
||||
*recovered,
|
||||
),
|
||||
recovered: *recovered,
|
||||
},
|
||||
VariantData::Tuple(fields, id) => {
|
||||
let ctor_id = self.lower_node_id(*id);
|
||||
self.alias_attrs(ctor_id, parent_id);
|
||||
@ -744,7 +723,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let (generics, kind, has_default) = match &i.kind {
|
||||
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
|
||||
let (generics, kind) = self.lower_generics(
|
||||
&generics,
|
||||
generics,
|
||||
Const::No,
|
||||
i.id,
|
||||
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
@ -761,27 +740,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
(generics, kind, expr.is_some())
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
|
||||
let asyncness = sig.header.asyncness;
|
||||
let names = self.lower_fn_params_to_names(&sig.decl);
|
||||
let (generics, sig) = self.lower_method_sig(
|
||||
generics,
|
||||
sig,
|
||||
i.id,
|
||||
FnDeclKind::Trait,
|
||||
asyncness.opt_return_id(),
|
||||
sig.header.coroutine_kind,
|
||||
);
|
||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
|
||||
let asyncness = sig.header.asyncness;
|
||||
let body_id =
|
||||
self.lower_maybe_async_body(i.span, hir_id, &sig.decl, asyncness, Some(&body));
|
||||
let body_id = self.lower_maybe_coroutine_body(
|
||||
i.span,
|
||||
hir_id,
|
||||
&sig.decl,
|
||||
sig.header.coroutine_kind,
|
||||
Some(body),
|
||||
);
|
||||
let (generics, sig) = self.lower_method_sig(
|
||||
generics,
|
||||
sig,
|
||||
i.id,
|
||||
FnDeclKind::Trait,
|
||||
asyncness.opt_return_id(),
|
||||
sig.header.coroutine_kind,
|
||||
);
|
||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
|
||||
}
|
||||
@ -857,7 +839,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
|
||||
let (generics, kind) = match &i.kind {
|
||||
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
|
||||
&generics,
|
||||
generics,
|
||||
Const::No,
|
||||
i.id,
|
||||
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
@ -870,13 +852,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
},
|
||||
),
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
|
||||
self.current_item = Some(i.span);
|
||||
let asyncness = sig.header.asyncness;
|
||||
let body_id = self.lower_maybe_async_body(
|
||||
let body_id = self.lower_maybe_coroutine_body(
|
||||
i.span,
|
||||
hir_id,
|
||||
&sig.decl,
|
||||
asyncness,
|
||||
sig.header.coroutine_kind,
|
||||
body.as_deref(),
|
||||
);
|
||||
let (generics, sig) = self.lower_method_sig(
|
||||
@ -884,7 +864,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
sig,
|
||||
i.id,
|
||||
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
|
||||
asyncness.opt_return_id(),
|
||||
sig.header.coroutine_kind,
|
||||
);
|
||||
|
||||
(generics, hir::ImplItemKind::Fn(sig, body_id))
|
||||
@ -899,7 +879,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| match ty {
|
||||
None => {
|
||||
let guar = this.tcx.sess.delay_span_bug(
|
||||
let guar = this.tcx.sess.span_delayed_bug(
|
||||
i.span,
|
||||
"expected to lower associated type, but it was missing",
|
||||
);
|
||||
@ -1032,7 +1012,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr<'hir> {
|
||||
match block {
|
||||
Some(block) => self.lower_block_expr(block),
|
||||
None => self.expr_err(span, self.tcx.sess.delay_span_bug(span, "no block")),
|
||||
None => self.expr_err(span, self.tcx.sess.span_delayed_bug(span, "no block")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1042,24 +1022,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
&[],
|
||||
match expr {
|
||||
Some(expr) => this.lower_expr_mut(expr),
|
||||
None => this.expr_err(span, this.tcx.sess.delay_span_bug(span, "no block")),
|
||||
None => this.expr_err(span, this.tcx.sess.span_delayed_bug(span, "no block")),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_maybe_async_body(
|
||||
/// Takes what may be the body of an `async fn` or a `gen fn` and wraps it in an `async {}` or
|
||||
/// `gen {}` block as appropriate.
|
||||
fn lower_maybe_coroutine_body(
|
||||
&mut self,
|
||||
span: Span,
|
||||
fn_id: hir::HirId,
|
||||
decl: &FnDecl,
|
||||
asyncness: Async,
|
||||
coroutine_kind: Option<CoroutineKind>,
|
||||
body: Option<&Block>,
|
||||
) -> hir::BodyId {
|
||||
let (closure_id, body) = match (asyncness, body) {
|
||||
(Async::Yes { closure_id, .. }, Some(body)) => (closure_id, body),
|
||||
_ => return self.lower_fn_body_block(span, decl, body),
|
||||
let (Some(coroutine_kind), Some(body)) = (coroutine_kind, body) else {
|
||||
return self.lower_fn_body_block(span, decl, body);
|
||||
};
|
||||
let closure_id = coroutine_kind.closure_id();
|
||||
|
||||
self.lower_body(|this| {
|
||||
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
|
||||
@ -1200,44 +1182,63 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
parameters.push(new_parameter);
|
||||
}
|
||||
|
||||
let async_expr = this.make_async_expr(
|
||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||
closure_id,
|
||||
None,
|
||||
body.span,
|
||||
hir::CoroutineSource::Fn,
|
||||
|this| {
|
||||
// Create a block from the user's function body:
|
||||
let user_body = this.lower_block_expr(body);
|
||||
let mkbody = |this: &mut LoweringContext<'_, 'hir>| {
|
||||
// Create a block from the user's function body:
|
||||
let user_body = this.lower_block_expr(body);
|
||||
|
||||
// Transform into `drop-temps { <user-body> }`, an expression:
|
||||
let desugared_span =
|
||||
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
|
||||
let user_body =
|
||||
this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
|
||||
// Transform into `drop-temps { <user-body> }`, an expression:
|
||||
let desugared_span =
|
||||
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
|
||||
let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
|
||||
|
||||
// As noted above, create the final block like
|
||||
//
|
||||
// ```
|
||||
// {
|
||||
// let $param_pattern = $raw_param;
|
||||
// ...
|
||||
// drop-temps { <user-body> }
|
||||
// }
|
||||
// ```
|
||||
let body = this.block_all(
|
||||
desugared_span,
|
||||
this.arena.alloc_from_iter(statements),
|
||||
Some(user_body),
|
||||
);
|
||||
// As noted above, create the final block like
|
||||
//
|
||||
// ```
|
||||
// {
|
||||
// let $param_pattern = $raw_param;
|
||||
// ...
|
||||
// drop-temps { <user-body> }
|
||||
// }
|
||||
// ```
|
||||
let body = this.block_all(
|
||||
desugared_span,
|
||||
this.arena.alloc_from_iter(statements),
|
||||
Some(user_body),
|
||||
);
|
||||
|
||||
this.expr_block(body)
|
||||
},
|
||||
);
|
||||
this.expr_block(body)
|
||||
};
|
||||
// FIXME(gen_blocks): Consider unifying the `make_*_expr` functions.
|
||||
let coroutine_expr = match coroutine_kind {
|
||||
CoroutineKind::Async { .. } => this.make_async_expr(
|
||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||
closure_id,
|
||||
None,
|
||||
body.span,
|
||||
hir::CoroutineSource::Fn,
|
||||
mkbody,
|
||||
),
|
||||
CoroutineKind::Gen { .. } => this.make_gen_expr(
|
||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||
closure_id,
|
||||
None,
|
||||
body.span,
|
||||
hir::CoroutineSource::Fn,
|
||||
mkbody,
|
||||
),
|
||||
CoroutineKind::AsyncGen { .. } => this.make_async_gen_expr(
|
||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||
closure_id,
|
||||
None,
|
||||
body.span,
|
||||
hir::CoroutineSource::Fn,
|
||||
mkbody,
|
||||
),
|
||||
};
|
||||
|
||||
let hir_id = this.lower_node_id(closure_id);
|
||||
this.maybe_forward_track_caller(body.span, fn_id, hir_id);
|
||||
let expr = hir::Expr { hir_id, kind: async_expr, span: this.lower_span(body.span) };
|
||||
let expr = hir::Expr { hir_id, kind: coroutine_expr, span: this.lower_span(body.span) };
|
||||
|
||||
(this.arena.alloc_from_iter(parameters), expr)
|
||||
})
|
||||
@ -1249,21 +1250,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
sig: &FnSig,
|
||||
id: NodeId,
|
||||
kind: FnDeclKind,
|
||||
is_async: Option<(NodeId, Span)>,
|
||||
coroutine_kind: Option<CoroutineKind>,
|
||||
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
|
||||
let header = self.lower_fn_header(sig.header);
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, decl) =
|
||||
self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
|
||||
this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async)
|
||||
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
|
||||
});
|
||||
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
|
||||
}
|
||||
|
||||
fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
|
||||
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
|
||||
hir::IsAsync::Async(span)
|
||||
} else {
|
||||
hir::IsAsync::NotAsync
|
||||
};
|
||||
hir::FnHeader {
|
||||
unsafety: self.lower_unsafety(h.unsafety),
|
||||
asyncness: self.lower_asyncness(h.asyncness),
|
||||
asyncness: asyncness,
|
||||
constness: self.lower_constness(h.constness),
|
||||
abi: self.lower_extern(h.ext),
|
||||
}
|
||||
@ -1305,13 +1311,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
});
|
||||
}
|
||||
|
||||
fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync {
|
||||
match a {
|
||||
Async::Yes { span, .. } => hir::IsAsync::Async(span),
|
||||
Async::No => hir::IsAsync::NotAsync,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
|
||||
match c {
|
||||
Const::Yes(_) => hir::Constness::Const,
|
||||
@ -1389,26 +1388,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let host_param_parts = if let Const::Yes(span) = constness
|
||||
&& self.tcx.features().effects
|
||||
{
|
||||
if let Some(param) =
|
||||
generics.params.iter().find(|x| x.attrs.iter().any(|x| x.has_name(sym::rustc_host)))
|
||||
{
|
||||
// user has manually specified a `rustc_host` param, in this case, we set
|
||||
// the param id so that lowering logic can use that. But we don't create
|
||||
// another host param, so this gives `None`.
|
||||
self.host_param_id = Some(self.local_def_id(param.id));
|
||||
None
|
||||
} else {
|
||||
let param_node_id = self.next_node_id();
|
||||
let hir_id = self.next_id();
|
||||
let def_id = self.create_def(
|
||||
self.local_def_id(parent_node_id),
|
||||
param_node_id,
|
||||
DefPathData::TypeNs(sym::host),
|
||||
span,
|
||||
);
|
||||
self.host_param_id = Some(def_id);
|
||||
Some((span, hir_id, def_id))
|
||||
}
|
||||
let span = self.lower_span(span);
|
||||
let param_node_id = self.next_node_id();
|
||||
let hir_id = self.next_id();
|
||||
let def_id = self.create_def(
|
||||
self.local_def_id(parent_node_id),
|
||||
param_node_id,
|
||||
sym::host,
|
||||
DefKind::ConstParam,
|
||||
span,
|
||||
);
|
||||
self.host_param_id = Some(def_id);
|
||||
Some((span, hir_id, def_id))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -1462,8 +1453,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
|
||||
if let Some((span, hir_id, def_id)) = host_param_parts {
|
||||
let const_node_id = self.next_node_id();
|
||||
let anon_const: LocalDefId =
|
||||
self.create_def(def_id, const_node_id, DefPathData::AnonConst, span);
|
||||
let anon_const =
|
||||
self.create_def(def_id, const_node_id, kw::Empty, DefKind::AnonConst, span);
|
||||
|
||||
let const_id = self.next_id();
|
||||
let const_expr_id = self.next_id();
|
||||
@ -1472,19 +1463,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
|
||||
self.children.push((anon_const, hir::MaybeOwner::NonOwner(const_id)));
|
||||
|
||||
let attr_id = self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id();
|
||||
|
||||
let attrs = self.arena.alloc_from_iter([Attribute {
|
||||
kind: AttrKind::Normal(P(NormalAttr::from_ident(Ident::new(
|
||||
sym::rustc_host,
|
||||
span,
|
||||
)))),
|
||||
span,
|
||||
id: attr_id,
|
||||
style: AttrStyle::Outer,
|
||||
}]);
|
||||
self.attrs.insert(hir_id.local_id, attrs);
|
||||
|
||||
let const_body = self.lower_body(|this| {
|
||||
(
|
||||
&[],
|
||||
@ -1526,6 +1504,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
hir_id: const_id,
|
||||
body: const_body,
|
||||
}),
|
||||
is_host_effect: true,
|
||||
},
|
||||
colon_span: None,
|
||||
pure_wrt_drop: false,
|
||||
|
||||
@ -30,12 +30,11 @@
|
||||
//! get confused if the spans from leaf AST nodes occur in multiple places
|
||||
//! in the HIR, especially for multiple identifiers.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![recursion_limit = "256"]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
@ -45,32 +44,24 @@ extern crate tracing;
|
||||
|
||||
use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait};
|
||||
|
||||
use rustc_ast::node_id::NodeMap;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit;
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{
|
||||
DiagnosticArgFromDisplay, DiagnosticMessage, Handler, StashKey, SubdiagnosticMessage,
|
||||
};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
use rustc_errors::{DiagnosticArgFromDisplay, StashKey};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
|
||||
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::{ConstArg, GenericArg, ItemLocalMap, ParamName, TraitCandidate};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_middle::{
|
||||
span_bug,
|
||||
ty::{ResolverAstLowering, TyCtxt},
|
||||
};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{DesugaringKind, Span, DUMMY_SP};
|
||||
use smallvec::SmallVec;
|
||||
@ -94,7 +85,7 @@ mod lifetime_collector;
|
||||
mod pat;
|
||||
mod path;
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
struct LoweringContext<'a, 'hir> {
|
||||
tcx: TyCtxt<'hir>,
|
||||
@ -128,26 +119,70 @@ struct LoweringContext<'a, 'hir> {
|
||||
|
||||
current_hir_id_owner: hir::OwnerId,
|
||||
item_local_id_counter: hir::ItemLocalId,
|
||||
trait_map: FxHashMap<ItemLocalId, Box<[TraitCandidate]>>,
|
||||
trait_map: ItemLocalMap<Box<[TraitCandidate]>>,
|
||||
|
||||
impl_trait_defs: Vec<hir::GenericParam<'hir>>,
|
||||
impl_trait_bounds: Vec<hir::WherePredicate<'hir>>,
|
||||
|
||||
/// NodeIds that are lowered inside the current HIR owner.
|
||||
node_id_to_local_id: FxHashMap<NodeId, hir::ItemLocalId>,
|
||||
node_id_to_local_id: NodeMap<hir::ItemLocalId>,
|
||||
|
||||
allow_try_trait: Option<Lrc<[Symbol]>>,
|
||||
allow_gen_future: Option<Lrc<[Symbol]>>,
|
||||
allow_try_trait: Lrc<[Symbol]>,
|
||||
allow_gen_future: Lrc<[Symbol]>,
|
||||
allow_async_iterator: Lrc<[Symbol]>,
|
||||
|
||||
/// Mapping from generics `def_id`s to TAIT generics `def_id`s.
|
||||
/// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
|
||||
/// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this
|
||||
/// field from the original parameter 'a to the new parameter 'a1.
|
||||
generics_def_id_map: Vec<FxHashMap<LocalDefId, LocalDefId>>,
|
||||
generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>,
|
||||
|
||||
host_param_id: Option<LocalDefId>,
|
||||
}
|
||||
|
||||
impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
|
||||
Self {
|
||||
// Pseudo-globals.
|
||||
tcx,
|
||||
resolver: resolver,
|
||||
arena: tcx.hir_arena,
|
||||
|
||||
// HirId handling.
|
||||
bodies: Vec::new(),
|
||||
attrs: SortedMap::default(),
|
||||
children: Vec::default(),
|
||||
current_hir_id_owner: hir::CRATE_OWNER_ID,
|
||||
item_local_id_counter: hir::ItemLocalId::new(0),
|
||||
node_id_to_local_id: Default::default(),
|
||||
trait_map: Default::default(),
|
||||
|
||||
// Lowering state.
|
||||
catch_scope: None,
|
||||
loop_scope: None,
|
||||
is_in_loop_condition: false,
|
||||
is_in_trait_impl: false,
|
||||
is_in_dyn_type: false,
|
||||
coroutine_kind: None,
|
||||
task_context: None,
|
||||
current_item: None,
|
||||
impl_trait_defs: Vec::new(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
|
||||
allow_gen_future: if tcx.features().async_fn_track_caller {
|
||||
[sym::gen_future, sym::closure_track_caller].into()
|
||||
} else {
|
||||
[sym::gen_future].into()
|
||||
},
|
||||
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
|
||||
// interact with `gen`/`async gen` blocks
|
||||
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
|
||||
generics_def_id_map: Default::default(),
|
||||
host_param_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ResolverAstLoweringExt {
|
||||
fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>>;
|
||||
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes>;
|
||||
@ -156,7 +191,6 @@ trait ResolverAstLoweringExt {
|
||||
fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
|
||||
fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>;
|
||||
fn remap_extra_lifetime_params(&mut self, from: NodeId, to: NodeId);
|
||||
fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind;
|
||||
}
|
||||
|
||||
impl ResolverAstLoweringExt for ResolverAstLowering {
|
||||
@ -220,10 +254,6 @@ impl ResolverAstLoweringExt for ResolverAstLowering {
|
||||
let lifetimes = self.extra_lifetime_params_map.remove(&from).unwrap_or_default();
|
||||
self.extra_lifetime_params_map.insert(to, lifetimes);
|
||||
}
|
||||
|
||||
fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind {
|
||||
self.builtin_macro_kinds.get(&def_id).copied().unwrap_or(MacroKind::Bang)
|
||||
}
|
||||
}
|
||||
|
||||
/// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree,
|
||||
@ -350,7 +380,7 @@ enum AstOwner<'a> {
|
||||
}
|
||||
|
||||
fn index_crate<'a>(
|
||||
node_id_to_def_id: &FxHashMap<NodeId, LocalDefId>,
|
||||
node_id_to_def_id: &NodeMap<LocalDefId>,
|
||||
krate: &'a Crate,
|
||||
) -> IndexVec<LocalDefId, AstOwner<'a>> {
|
||||
let mut indexer = Indexer { node_id_to_def_id, index: IndexVec::new() };
|
||||
@ -360,7 +390,7 @@ fn index_crate<'a>(
|
||||
return indexer.index;
|
||||
|
||||
struct Indexer<'s, 'a> {
|
||||
node_id_to_def_id: &'s FxHashMap<NodeId, LocalDefId>,
|
||||
node_id_to_def_id: &'s NodeMap<LocalDefId>,
|
||||
index: IndexVec<LocalDefId, AstOwner<'a>>,
|
||||
}
|
||||
|
||||
@ -421,6 +451,7 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
|
||||
tcx.ensure_with_value().output_filenames(());
|
||||
tcx.ensure_with_value().early_lint_checks(());
|
||||
tcx.ensure_with_value().debugger_visualizers(LOCAL_CRATE);
|
||||
tcx.ensure_with_value().get_lang_items(());
|
||||
let (mut resolver, krate) = tcx.resolver_for_lowering(()).steal();
|
||||
|
||||
let ast_index = index_crate(&resolver.node_id_to_def_id, &krate);
|
||||
@ -443,11 +474,6 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
|
||||
drop(ast_index);
|
||||
sess.time("drop_ast", || drop(krate));
|
||||
|
||||
// Discard hygiene data, which isn't required after lowering to HIR.
|
||||
if !sess.opts.unstable_opts.keep_hygiene_data {
|
||||
rustc_span::hygiene::clear_syntax_context_map();
|
||||
}
|
||||
|
||||
// Don't hash unless necessary, because it's expensive.
|
||||
let opt_hir_hash =
|
||||
if tcx.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None };
|
||||
@ -474,19 +500,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
&mut self,
|
||||
parent: LocalDefId,
|
||||
node_id: ast::NodeId,
|
||||
data: DefPathData,
|
||||
name: Symbol,
|
||||
def_kind: DefKind,
|
||||
span: Span,
|
||||
) -> LocalDefId {
|
||||
debug_assert_ne!(node_id, ast::DUMMY_NODE_ID);
|
||||
assert!(
|
||||
self.opt_local_def_id(node_id).is_none(),
|
||||
"adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
|
||||
"adding a def'n for node-id {:?} and def kind {:?} but a previous def'n exists: {:?}",
|
||||
node_id,
|
||||
data,
|
||||
def_kind,
|
||||
self.tcx.hir().def_key(self.local_def_id(node_id)),
|
||||
);
|
||||
|
||||
let def_id = self.tcx.at(span).create_def(parent, data).def_id();
|
||||
let def_id = self.tcx.at(span).create_def(parent, name, def_kind).def_id();
|
||||
|
||||
debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
|
||||
self.resolver.node_id_to_def_id.insert(node_id, def_id);
|
||||
@ -504,7 +531,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
/// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name
|
||||
/// resolver (if any).
|
||||
fn orig_opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
|
||||
self.resolver.node_id_to_def_id.get(&node).map(|local_def_id| *local_def_id)
|
||||
self.resolver.node_id_to_def_id.get(&node).copied()
|
||||
}
|
||||
|
||||
/// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name
|
||||
@ -547,7 +574,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.generics_def_id_map
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|map| map.get(&local_def_id).map(|local_def_id| *local_def_id))
|
||||
.find_map(|map| map.get(&local_def_id).copied())
|
||||
.unwrap_or(local_def_id)
|
||||
}
|
||||
|
||||
@ -615,7 +642,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
/// `'a` declared on the TAIT, instead of the function.
|
||||
fn with_remapping<R>(
|
||||
&mut self,
|
||||
remap: FxHashMap<LocalDefId, LocalDefId>,
|
||||
remap: LocalDefIdMap<LocalDefId>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
self.generics_def_id_map.push(remap);
|
||||
@ -737,8 +764,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.resolver.get_import_res(id).present_items()
|
||||
}
|
||||
|
||||
fn diagnostic(&self) -> &Handler {
|
||||
self.tcx.sess.diagnostic()
|
||||
fn make_lang_item_path(
|
||||
&mut self,
|
||||
lang_item: hir::LangItem,
|
||||
span: Span,
|
||||
args: Option<&'hir hir::GenericArgs<'hir>>,
|
||||
) -> &'hir hir::Path<'hir> {
|
||||
let def_id = self.tcx.require_lang_item(lang_item, Some(span));
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
let res = Res::Def(def_kind, def_id);
|
||||
self.arena.alloc(hir::Path {
|
||||
span,
|
||||
res,
|
||||
segments: self.arena.alloc_from_iter([hir::PathSegment {
|
||||
ident: Ident::new(lang_item.name(), span),
|
||||
hir_id: self.next_id(),
|
||||
res,
|
||||
args,
|
||||
infer_args: false,
|
||||
}]),
|
||||
})
|
||||
}
|
||||
|
||||
/// Reuses the span but adds information like the kind of the desugaring and features that are
|
||||
@ -787,7 +832,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let _def_id = self.create_def(
|
||||
self.current_hir_id_owner.def_id,
|
||||
param,
|
||||
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
|
||||
kw::UnderscoreLifetime,
|
||||
DefKind::LifetimeParam,
|
||||
ident.span,
|
||||
);
|
||||
debug!(?_def_id);
|
||||
@ -851,7 +897,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
result
|
||||
}
|
||||
|
||||
fn with_new_scopes<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
|
||||
fn with_new_scopes<T>(&mut self, scope_span: Span, f: impl FnOnce(&mut Self) -> T) -> T {
|
||||
let current_item = self.current_item;
|
||||
self.current_item = Some(scope_span);
|
||||
|
||||
let was_in_loop_condition = self.is_in_loop_condition;
|
||||
self.is_in_loop_condition = false;
|
||||
|
||||
@ -863,6 +912,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
|
||||
self.is_in_loop_condition = was_in_loop_condition;
|
||||
|
||||
self.current_item = current_item;
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
@ -1162,7 +1213,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
itctx: &ImplTraitContext,
|
||||
) -> hir::GenericArg<'hir> {
|
||||
match arg {
|
||||
ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(<)),
|
||||
ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)),
|
||||
ast::GenericArg::Type(ty) => {
|
||||
match &ty.kind {
|
||||
TyKind::Infer if self.tcx.features().generic_arg_infer => {
|
||||
@ -1199,7 +1250,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let def_id = self.create_def(
|
||||
parent_def_id.def_id,
|
||||
node_id,
|
||||
DefPathData::AnonConst,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
span,
|
||||
);
|
||||
|
||||
@ -1211,7 +1263,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
tokens: None,
|
||||
};
|
||||
|
||||
let ct = self.with_new_scopes(|this| hir::AnonConst {
|
||||
let ct = self.with_new_scopes(span, |this| hir::AnonConst {
|
||||
def_id,
|
||||
hir_id: this.lower_node_id(node_id),
|
||||
body: this.lower_const_body(path_expr.span, Some(&path_expr)),
|
||||
@ -1226,10 +1278,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
GenericArg::Type(self.lower_ty(&ty, itctx))
|
||||
GenericArg::Type(self.lower_ty(ty, itctx))
|
||||
}
|
||||
ast::GenericArg::Const(ct) => GenericArg::Const(ConstArg {
|
||||
value: self.lower_anon_const(&ct),
|
||||
value: self.lower_anon_const(ct),
|
||||
span: self.lower_span(ct.value.span),
|
||||
is_desugared_from_effects: false,
|
||||
}),
|
||||
@ -1272,7 +1324,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let lifetime_bound = this.elided_dyn_bound(t.span);
|
||||
(bounds, lifetime_bound)
|
||||
});
|
||||
let kind = hir::TyKind::TraitObject(bounds, &lifetime_bound, TraitObjectSyntax::None);
|
||||
let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None);
|
||||
return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() };
|
||||
}
|
||||
|
||||
@ -1293,7 +1345,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let kind = match &t.kind {
|
||||
TyKind::Infer => hir::TyKind::Infer,
|
||||
TyKind::Err => {
|
||||
hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered"))
|
||||
hir::TyKind::Err(self.tcx.sess.span_delayed_bug(t.span, "TyKind::Err lowered"))
|
||||
}
|
||||
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
@ -1374,7 +1426,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
GenericBound::Trait(
|
||||
ty,
|
||||
modifier @ (TraitBoundModifier::None
|
||||
| TraitBoundModifier::MaybeConst
|
||||
| TraitBoundModifier::MaybeConst(_)
|
||||
| TraitBoundModifier::Negative),
|
||||
) => {
|
||||
Some(this.lower_poly_trait_ref(ty, itctx, modifier.to_constness()))
|
||||
@ -1436,7 +1488,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.create_def(
|
||||
self.current_hir_id_owner.def_id,
|
||||
*def_node_id,
|
||||
DefPathData::TypeNs(ident.name),
|
||||
ident.name,
|
||||
DefKind::TyParam,
|
||||
span,
|
||||
);
|
||||
let (param, bounds, path) = self.lower_universal_param_and_bounds(
|
||||
@ -1476,7 +1529,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
TyKind::MacCall(_) => panic!("`TyKind::MacCall` should have been expanded by now"),
|
||||
TyKind::CVarArgs => {
|
||||
let guar = self.tcx.sess.delay_span_bug(
|
||||
let guar = self.tcx.sess.span_delayed_bug(
|
||||
t.span,
|
||||
"`TyKind::CVarArgs` should have been handled elsewhere",
|
||||
);
|
||||
@ -1542,8 +1595,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
Vec::new()
|
||||
}
|
||||
hir::OpaqueTyOrigin::FnReturn(..) => {
|
||||
if let FnDeclKind::Impl | FnDeclKind::Trait =
|
||||
fn_kind.expect("expected RPITs to be lowered with a FnKind")
|
||||
if matches!(
|
||||
fn_kind.expect("expected RPITs to be lowered with a FnKind"),
|
||||
FnDeclKind::Impl | FnDeclKind::Trait
|
||||
) || self.tcx.features().lifetime_capture_rules_2024
|
||||
|| span.at_least_rust_2024()
|
||||
{
|
||||
// return-position impl trait in trait was decided to capture all
|
||||
// in-scope lifetimes, which we collect for all opaques during resolution.
|
||||
@ -1556,7 +1612,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
|
||||
// example, we only need to duplicate lifetimes that appear in the
|
||||
// bounds, since those are the only ones that are captured by the opaque.
|
||||
lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds)
|
||||
lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
|
||||
}
|
||||
}
|
||||
hir::OpaqueTyOrigin::AsyncFn(..) => {
|
||||
@ -1589,14 +1645,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let opaque_ty_def_id = self.create_def(
|
||||
self.current_hir_id_owner.def_id,
|
||||
opaque_ty_node_id,
|
||||
DefPathData::ImplTrait,
|
||||
kw::Empty,
|
||||
DefKind::OpaqueTy,
|
||||
opaque_ty_span,
|
||||
);
|
||||
debug!(?opaque_ty_def_id);
|
||||
|
||||
// Map from captured (old) lifetime to synthetic (new) lifetime.
|
||||
// Used to resolve lifetimes in the bounds of the opaque.
|
||||
let mut captured_to_synthesized_mapping = FxHashMap::default();
|
||||
let mut captured_to_synthesized_mapping = LocalDefIdMap::default();
|
||||
// List of (early-bound) synthetic lifetimes that are owned by the opaque.
|
||||
// This is used to create the `hir::Generics` owned by the opaque.
|
||||
let mut synthesized_lifetime_definitions = vec![];
|
||||
@ -1618,7 +1675,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
} else {
|
||||
self.tcx
|
||||
.sess
|
||||
.delay_span_bug(lifetime.ident.span, "no def-id for fresh lifetime");
|
||||
.span_delayed_bug(lifetime.ident.span, "no def-id for fresh lifetime");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -1643,8 +1700,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let duplicated_lifetime_def_id = self.create_def(
|
||||
opaque_ty_def_id,
|
||||
duplicated_lifetime_node_id,
|
||||
DefPathData::LifetimeNs(lifetime.ident.name),
|
||||
lifetime.ident.span,
|
||||
lifetime.ident.name,
|
||||
DefKind::LifetimeParam,
|
||||
self.lower_span(lifetime.ident.span),
|
||||
);
|
||||
captured_to_synthesized_mapping.insert(old_def_id, duplicated_lifetime_def_id);
|
||||
// FIXME: Instead of doing this, we could move this whole loop
|
||||
@ -1653,7 +1711,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
synthesized_lifetime_definitions.push((
|
||||
duplicated_lifetime_node_id,
|
||||
duplicated_lifetime_def_id,
|
||||
lifetime.ident,
|
||||
self.lower_ident(lifetime.ident),
|
||||
));
|
||||
|
||||
// Now make an arg that we can use for the generic params of the opaque tykind.
|
||||
@ -1747,13 +1805,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}))
|
||||
}
|
||||
|
||||
// Lowers a function declaration.
|
||||
//
|
||||
// `decl`: the unlowered (AST) function declaration.
|
||||
// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given `NodeId`.
|
||||
// `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future<Output = T>` in the
|
||||
// return type. This is used for `async fn` declarations. The `NodeId` is the ID of the
|
||||
// return type `impl Trait` item, and the `Span` points to the `async` keyword.
|
||||
/// Lowers a function declaration.
|
||||
///
|
||||
/// `decl`: the unlowered (AST) function declaration.
|
||||
///
|
||||
/// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given
|
||||
/// `NodeId`.
|
||||
///
|
||||
/// `transform_return_type`: if `Some`, applies some conversion to the return type, such as is
|
||||
/// needed for `async fn` and `gen fn`. See [`CoroutineKind`] for more details.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn lower_fn_decl(
|
||||
&mut self,
|
||||
@ -1761,7 +1821,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
fn_node_id: NodeId,
|
||||
fn_span: Span,
|
||||
kind: FnDeclKind,
|
||||
make_ret_async: Option<(NodeId, Span)>,
|
||||
coro: Option<CoroutineKind>,
|
||||
) -> &'hir hir::FnDecl<'hir> {
|
||||
let c_variadic = decl.c_variadic();
|
||||
|
||||
@ -1790,11 +1850,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.lower_ty_direct(¶m.ty, &itctx)
|
||||
}));
|
||||
|
||||
let output = if let Some((ret_id, _span)) = make_ret_async {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
self.lower_async_fn_ret_ty(&decl.output, fn_def_id, ret_id, kind, fn_span)
|
||||
} else {
|
||||
match &decl.output {
|
||||
let output = match coro {
|
||||
Some(coro) => {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind, fn_span)
|
||||
}
|
||||
None => match &decl.output {
|
||||
FnRetTy::Ty(ty) => {
|
||||
let context = if kind.return_impl_trait_allowed() {
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
@ -1818,7 +1879,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
hir::FnRetTy::Return(self.lower_ty(ty, &context))
|
||||
}
|
||||
FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
self.arena.alloc(hir::FnDecl {
|
||||
@ -1857,16 +1918,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// `fn_node_id`: `NodeId` of the parent function (used to create child impl trait definition)
|
||||
// `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn lower_async_fn_ret_ty(
|
||||
fn lower_coroutine_fn_ret_ty(
|
||||
&mut self,
|
||||
output: &FnRetTy,
|
||||
fn_def_id: LocalDefId,
|
||||
opaque_ty_node_id: NodeId,
|
||||
coro: CoroutineKind,
|
||||
fn_kind: FnDeclKind,
|
||||
fn_span: Span,
|
||||
) -> hir::FnRetTy<'hir> {
|
||||
let span = self.lower_span(fn_span);
|
||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
|
||||
|
||||
let (opaque_ty_node_id, allowed_features) = match coro {
|
||||
CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None),
|
||||
CoroutineKind::Gen { return_impl_trait_id, .. } => (return_impl_trait_id, None),
|
||||
CoroutineKind::AsyncGen { return_impl_trait_id, .. } => {
|
||||
(return_impl_trait_id, Some(self.allow_async_iterator.clone()))
|
||||
}
|
||||
};
|
||||
|
||||
let opaque_ty_span =
|
||||
self.mark_span_with_reason(DesugaringKind::Async, span, allowed_features);
|
||||
|
||||
let captured_lifetimes: Vec<_> = self
|
||||
.resolver
|
||||
@ -1883,15 +1954,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
span,
|
||||
opaque_ty_span,
|
||||
|this| {
|
||||
let future_bound = this.lower_async_fn_output_type_to_future_bound(
|
||||
let bound = this.lower_coroutine_fn_output_type_to_bound(
|
||||
output,
|
||||
span,
|
||||
coro,
|
||||
opaque_ty_span,
|
||||
ImplTraitContext::ReturnPositionOpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
||||
fn_kind,
|
||||
},
|
||||
);
|
||||
arena_vec![this; future_bound]
|
||||
arena_vec![this; bound]
|
||||
},
|
||||
);
|
||||
|
||||
@ -1900,10 +1972,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
|
||||
/// Transforms `-> T` into `Future<Output = T>`.
|
||||
fn lower_async_fn_output_type_to_future_bound(
|
||||
fn lower_coroutine_fn_output_type_to_bound(
|
||||
&mut self,
|
||||
output: &FnRetTy,
|
||||
span: Span,
|
||||
coro: CoroutineKind,
|
||||
opaque_ty_span: Span,
|
||||
nested_impl_trait_context: ImplTraitContext,
|
||||
) -> hir::GenericBound<'hir> {
|
||||
// Compute the `T` in `Future<Output = T>` from the return type.
|
||||
@ -1917,20 +1990,34 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])),
|
||||
};
|
||||
|
||||
// "<Output = T>"
|
||||
let future_args = self.arena.alloc(hir::GenericArgs {
|
||||
// "<$assoc_ty_name = T>"
|
||||
let (assoc_ty_name, trait_lang_item) = match coro {
|
||||
CoroutineKind::Async { .. } => (sym::Output, hir::LangItem::Future),
|
||||
CoroutineKind::Gen { .. } => (sym::Item, hir::LangItem::Iterator),
|
||||
CoroutineKind::AsyncGen { .. } => (sym::Item, hir::LangItem::AsyncIterator),
|
||||
};
|
||||
|
||||
let bound_args = self.arena.alloc(hir::GenericArgs {
|
||||
args: &[],
|
||||
bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
|
||||
bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, opaque_ty_span, output_ty)],
|
||||
parenthesized: hir::GenericArgsParentheses::No,
|
||||
span_ext: DUMMY_SP,
|
||||
});
|
||||
|
||||
hir::GenericBound::LangItemTrait(
|
||||
// ::std::future::Future<future_params>
|
||||
hir::LangItem::Future,
|
||||
self.lower_span(span),
|
||||
self.next_id(),
|
||||
future_args,
|
||||
hir::GenericBound::Trait(
|
||||
hir::PolyTraitRef {
|
||||
bound_generic_params: &[],
|
||||
trait_ref: hir::TraitRef {
|
||||
path: self.make_lang_item_path(
|
||||
trait_lang_item,
|
||||
opaque_ty_span,
|
||||
Some(bound_args),
|
||||
),
|
||||
hir_ref_id: self.next_id(),
|
||||
},
|
||||
span: opaque_ty_span,
|
||||
},
|
||||
hir::TraitBoundModifier::None,
|
||||
)
|
||||
}
|
||||
|
||||
@ -2072,14 +2159,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
(hir::ParamName::Plain(self.lower_ident(param.ident)), kind)
|
||||
}
|
||||
GenericParamKind::Const { ty, kw_span: _, default } => {
|
||||
let ty = self.lower_ty(
|
||||
&ty,
|
||||
&ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault),
|
||||
);
|
||||
let ty = self
|
||||
.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault));
|
||||
let default = default.as_ref().map(|def| self.lower_anon_const(def));
|
||||
(
|
||||
hir::ParamName::Plain(self.lower_ident(param.ident)),
|
||||
hir::GenericParamKind::Const { ty, default },
|
||||
hir::GenericParamKind::Const { ty, default, is_host_effect: false },
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -2200,7 +2285,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
match c.value.kind {
|
||||
ExprKind::Underscore => {
|
||||
if self.tcx.features().generic_arg_infer {
|
||||
hir::ArrayLen::Infer(self.lower_node_id(c.id), c.value.span)
|
||||
hir::ArrayLen::Infer(self.lower_node_id(c.id), self.lower_span(c.value.span))
|
||||
} else {
|
||||
feature_err(
|
||||
&self.tcx.sess.parse_sess,
|
||||
@ -2217,7 +2302,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
|
||||
fn lower_anon_const(&mut self, c: &AnonConst) -> hir::AnonConst {
|
||||
self.with_new_scopes(|this| hir::AnonConst {
|
||||
self.with_new_scopes(c.value.span, |this| hir::AnonConst {
|
||||
def_id: this.local_def_id(c.id),
|
||||
hir_id: this.lower_node_id(c.id),
|
||||
body: this.lower_const_body(c.value.span, Some(&c.value)),
|
||||
@ -2234,7 +2319,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
fn lower_trait_bound_modifier(&mut self, f: TraitBoundModifier) -> hir::TraitBoundModifier {
|
||||
match f {
|
||||
TraitBoundModifier::None => hir::TraitBoundModifier::None,
|
||||
TraitBoundModifier::MaybeConst => hir::TraitBoundModifier::MaybeConst,
|
||||
TraitBoundModifier::MaybeConst(_) => hir::TraitBoundModifier::MaybeConst,
|
||||
|
||||
TraitBoundModifier::Negative => {
|
||||
if self.tcx.features().negative_bounds {
|
||||
@ -2311,21 +2396,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
|
||||
fn pat_cf_continue(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
|
||||
let field = self.single_pat_field(span, pat);
|
||||
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowContinue, field, None)
|
||||
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowContinue, field)
|
||||
}
|
||||
|
||||
fn pat_cf_break(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
|
||||
let field = self.single_pat_field(span, pat);
|
||||
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowBreak, field, None)
|
||||
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowBreak, field)
|
||||
}
|
||||
|
||||
fn pat_some(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
|
||||
let field = self.single_pat_field(span, pat);
|
||||
self.pat_lang_item_variant(span, hir::LangItem::OptionSome, field, None)
|
||||
self.pat_lang_item_variant(span, hir::LangItem::OptionSome, field)
|
||||
}
|
||||
|
||||
fn pat_none(&mut self, span: Span) -> &'hir hir::Pat<'hir> {
|
||||
self.pat_lang_item_variant(span, hir::LangItem::OptionNone, &[], None)
|
||||
self.pat_lang_item_variant(span, hir::LangItem::OptionNone, &[])
|
||||
}
|
||||
|
||||
fn single_pat_field(
|
||||
@ -2348,9 +2433,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
span: Span,
|
||||
lang_item: hir::LangItem,
|
||||
fields: &'hir [hir::PatField<'hir>],
|
||||
hir_id: Option<hir::HirId>,
|
||||
) -> &'hir hir::Pat<'hir> {
|
||||
let qpath = hir::QPath::LangItem(lang_item, self.lower_span(span), hir_id);
|
||||
let qpath = hir::QPath::LangItem(lang_item, self.lower_span(span));
|
||||
self.pat(span, hir::PatKind::Struct(qpath, fields, false))
|
||||
}
|
||||
|
||||
@ -2482,9 +2566,10 @@ impl<'hir> GenericArgsCtor<'hir> {
|
||||
let hir_id = lcx.next_id();
|
||||
|
||||
let Some(host_param_id) = lcx.host_param_id else {
|
||||
lcx.tcx
|
||||
.sess
|
||||
.delay_span_bug(span, "no host param id for call in const yet no errors reported");
|
||||
lcx.tcx.sess.span_delayed_bug(
|
||||
span,
|
||||
"no host param id for call in const yet no errors reported",
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
@ -2507,17 +2592,14 @@ impl<'hir> GenericArgsCtor<'hir> {
|
||||
})
|
||||
});
|
||||
|
||||
let attr_id = lcx.tcx.sess.parse_sess.attr_id_generator.mk_attr_id();
|
||||
let attr = lcx.arena.alloc(Attribute {
|
||||
kind: AttrKind::Normal(P(NormalAttr::from_ident(Ident::new(sym::rustc_host, span)))),
|
||||
let def_id = lcx.create_def(
|
||||
lcx.current_hir_id_owner.def_id,
|
||||
id,
|
||||
kw::Empty,
|
||||
DefKind::AnonConst,
|
||||
span,
|
||||
id: attr_id,
|
||||
style: AttrStyle::Outer,
|
||||
});
|
||||
lcx.attrs.insert(hir_id.local_id, std::slice::from_ref(attr));
|
||||
);
|
||||
|
||||
let def_id =
|
||||
lcx.create_def(lcx.current_hir_id_owner.def_id, id, DefPathData::AnonConst, span);
|
||||
lcx.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
|
||||
self.args.push(hir::GenericArg::Const(hir::ConstArg {
|
||||
value: hir::AnonConst { def_id, hir_id, body },
|
||||
|
||||
@ -108,7 +108,7 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lifetimes_in_bounds(
|
||||
pub(crate) fn lifetimes_in_bounds(
|
||||
resolver: &ResolverAstLowering,
|
||||
bounds: &GenericBounds,
|
||||
) -> Vec<Lifetime> {
|
||||
|
||||
@ -18,12 +18,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.arena.alloc(self.lower_pat_mut(pattern))
|
||||
}
|
||||
|
||||
pub(crate) fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
|
||||
fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
|
||||
ensure_sufficient_stack(|| {
|
||||
// loop here to avoid recursion
|
||||
let node = loop {
|
||||
match &pattern.kind {
|
||||
PatKind::Wild => break hir::PatKind::Wild,
|
||||
PatKind::Never => break hir::PatKind::Never,
|
||||
PatKind::Ident(binding_mode, ident, sub) => {
|
||||
let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(s));
|
||||
break self.lower_pat_ident(pattern, *binding_mode, *ident, lower_sub);
|
||||
|
||||
@ -9,6 +9,7 @@ use rustc_ast::{self as ast, *};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, PartialRes, Res};
|
||||
use rustc_hir::GenericArg;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::{BytePos, Span, DUMMY_SP};
|
||||
|
||||
@ -139,7 +140,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
|
||||
// We should've returned in the for loop above.
|
||||
|
||||
self.diagnostic().span_bug(
|
||||
self.tcx.sess.dcx().span_bug(
|
||||
p.span,
|
||||
format!(
|
||||
"lower_qpath: no final extension segment in {}..{}",
|
||||
@ -285,7 +286,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let (start, end) = match self.resolver.get_lifetime_res(segment_id) {
|
||||
Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end),
|
||||
None => return,
|
||||
Some(_) => panic!(),
|
||||
Some(res) => {
|
||||
span_bug!(path_span, "expected an elided lifetime to insert. found {res:?}")
|
||||
}
|
||||
};
|
||||
let expected_lifetimes = end.as_usize() - start.as_usize();
|
||||
debug!(expected_lifetimes);
|
||||
@ -372,10 +375,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// ```
|
||||
FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::ReturnPositionOpaqueTy { .. }) => {
|
||||
if self.tcx.features().impl_trait_in_fn_trait_return {
|
||||
self.lower_ty(&ty, itctx)
|
||||
self.lower_ty(ty, itctx)
|
||||
} else {
|
||||
self.lower_ty(
|
||||
&ty,
|
||||
ty,
|
||||
&ImplTraitContext::FeatureGated(
|
||||
ImplTraitPosition::FnTraitReturn,
|
||||
sym::impl_trait_in_fn_trait_return,
|
||||
@ -384,12 +387,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
}
|
||||
FnRetTy::Ty(ty) => {
|
||||
self.lower_ty(&ty, &ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn))
|
||||
self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn))
|
||||
}
|
||||
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
|
||||
};
|
||||
let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
|
||||
let binding = self.output_ty_binding(output_ty.span, output_ty);
|
||||
let binding = self.assoc_ty_binding(sym::Output, output_ty.span, output_ty);
|
||||
(
|
||||
GenericArgsCtor {
|
||||
args,
|
||||
@ -401,13 +404,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
)
|
||||
}
|
||||
|
||||
/// An associated type binding `Output = $ty`.
|
||||
pub(crate) fn output_ty_binding(
|
||||
/// An associated type binding `$assoc_ty_name = $ty`.
|
||||
pub(crate) fn assoc_ty_binding(
|
||||
&mut self,
|
||||
assoc_ty_name: rustc_span::Symbol,
|
||||
span: Span,
|
||||
ty: &'hir hir::Ty<'hir>,
|
||||
) -> hir::TypeBinding<'hir> {
|
||||
let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME);
|
||||
let ident = Ident::with_dummy_span(assoc_ty_name);
|
||||
let kind = hir::TypeBindingKind::Equality { term: ty.into() };
|
||||
let args = arena_vec![self;];
|
||||
let bindings = arena_vec![self;];
|
||||
|
||||
@ -5,7 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.10.1"
|
||||
itertools = "0.11"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
||||
@ -174,6 +174,10 @@ ast_passes_item_underscore = `{$kind}` items in this context need a name
|
||||
ast_passes_keyword_lifetime =
|
||||
lifetimes cannot use keyword names
|
||||
|
||||
ast_passes_match_arm_with_no_body =
|
||||
`match` arm with no body
|
||||
.suggestion = add a body after the pattern
|
||||
|
||||
ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name
|
||||
.help = consider using the `#[path]` attribute to specify filesystem path
|
||||
|
||||
@ -218,9 +222,13 @@ ast_passes_static_without_body =
|
||||
.suggestion = provide a definition for the static
|
||||
|
||||
ast_passes_tilde_const_disallowed = `~const` is not allowed here
|
||||
.trait = trait objects cannot have `~const` trait bounds
|
||||
.closure = closures cannot have `~const` trait bounds
|
||||
.function = this function is not `const`, so it cannot have `~const` trait bounds
|
||||
.trait = this trait is not a `#[const_trait]`, so it cannot have `~const` trait bounds
|
||||
.trait_impl = this impl is not `const`, so it cannot have `~const` trait bounds
|
||||
.impl = inherent impls cannot have `~const` trait bounds
|
||||
.object = trait objects cannot have `~const` trait bounds
|
||||
.item = this item cannot have `~const` trait bounds
|
||||
|
||||
ast_passes_trait_fn_const =
|
||||
functions in traits cannot be declared const
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
|
||||
use itertools::{Either, Itertools};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
|
||||
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
|
||||
use rustc_ast::walk_list;
|
||||
use rustc_ast::*;
|
||||
use rustc_ast::{walk_list, StaticItem};
|
||||
use rustc_ast_pretty::pprust::{self, State};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_feature::Features;
|
||||
@ -40,6 +40,10 @@ enum SelfSemantic {
|
||||
enum DisallowTildeConstContext<'a> {
|
||||
TraitObject,
|
||||
Fn(FnKind<'a>),
|
||||
Trait(Span),
|
||||
TraitImpl(Span),
|
||||
Impl(Span),
|
||||
Item,
|
||||
}
|
||||
|
||||
struct AstValidator<'a> {
|
||||
@ -110,18 +114,6 @@ impl<'a> AstValidator<'a> {
|
||||
self.disallow_tilde_const = old;
|
||||
}
|
||||
|
||||
fn with_tilde_const_allowed(&mut self, f: impl FnOnce(&mut Self)) {
|
||||
self.with_tilde_const(None, f)
|
||||
}
|
||||
|
||||
fn with_banned_tilde_const(
|
||||
&mut self,
|
||||
ctx: DisallowTildeConstContext<'a>,
|
||||
f: impl FnOnce(&mut Self),
|
||||
) {
|
||||
self.with_tilde_const(Some(ctx), f)
|
||||
}
|
||||
|
||||
fn check_type_alias_where_clause_location(
|
||||
&mut self,
|
||||
ty_alias: &TyAlias,
|
||||
@ -173,7 +165,7 @@ impl<'a> AstValidator<'a> {
|
||||
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
|
||||
}
|
||||
TyKind::TraitObject(..) => self
|
||||
.with_banned_tilde_const(DisallowTildeConstContext::TraitObject, |this| {
|
||||
.with_tilde_const(Some(DisallowTildeConstContext::TraitObject), |this| {
|
||||
visit::walk_ty(this, t)
|
||||
}),
|
||||
TyKind::Path(qself, path) => {
|
||||
@ -229,8 +221,8 @@ impl<'a> AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn err_handler(&self) -> &rustc_errors::Handler {
|
||||
&self.session.diagnostic()
|
||||
fn dcx(&self) -> &rustc_errors::DiagCtxt {
|
||||
self.session.dcx()
|
||||
}
|
||||
|
||||
fn check_lifetime(&self, ident: Ident) {
|
||||
@ -278,7 +270,7 @@ impl<'a> AstValidator<'a> {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
|
||||
self.dcx().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
|
||||
}
|
||||
|
||||
fn deny_anon_struct_or_union(&self, ty: &Ty) {
|
||||
@ -287,15 +279,14 @@ impl<'a> AstValidator<'a> {
|
||||
TyKind::AnonUnion(..) => "union",
|
||||
_ => return,
|
||||
};
|
||||
self.err_handler()
|
||||
.emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
|
||||
self.dcx().emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
|
||||
}
|
||||
|
||||
fn deny_unnamed_field(&self, field: &FieldDef) {
|
||||
if let Some(ident) = field.ident
|
||||
&& ident.name == kw::Underscore
|
||||
{
|
||||
self.err_handler()
|
||||
self.dcx()
|
||||
.emit_err(errors::InvalidUnnamedField { span: field.span, ident_span: ident.span });
|
||||
}
|
||||
}
|
||||
@ -401,7 +392,7 @@ impl<'a> AstValidator<'a> {
|
||||
[b0] => b0.span(),
|
||||
[b0, .., bl] => b0.span().to(bl.span()),
|
||||
};
|
||||
self.err_handler().emit_err(errors::BoundInContext { span, ctx });
|
||||
self.dcx().emit_err(errors::BoundInContext { span, ctx });
|
||||
}
|
||||
|
||||
fn check_foreign_ty_genericless(
|
||||
@ -411,7 +402,7 @@ impl<'a> AstValidator<'a> {
|
||||
after_where_clause: &TyAliasWhereClause,
|
||||
) {
|
||||
let cannot_have = |span, descr, remove_descr| {
|
||||
self.err_handler().emit_err(errors::ExternTypesCannotHave {
|
||||
self.dcx().emit_err(errors::ExternTypesCannotHave {
|
||||
span,
|
||||
descr,
|
||||
remove_descr,
|
||||
@ -437,7 +428,7 @@ impl<'a> AstValidator<'a> {
|
||||
let Some(body) = body else {
|
||||
return;
|
||||
};
|
||||
self.err_handler().emit_err(errors::BodyInExtern {
|
||||
self.dcx().emit_err(errors::BodyInExtern {
|
||||
span: ident.span,
|
||||
body,
|
||||
block: self.current_extern_span(),
|
||||
@ -450,7 +441,7 @@ impl<'a> AstValidator<'a> {
|
||||
let Some(body) = body else {
|
||||
return;
|
||||
};
|
||||
self.err_handler().emit_err(errors::FnBodyInExtern {
|
||||
self.dcx().emit_err(errors::FnBodyInExtern {
|
||||
span: ident.span,
|
||||
body: body.span,
|
||||
block: self.current_extern_span(),
|
||||
@ -464,7 +455,7 @@ impl<'a> AstValidator<'a> {
|
||||
/// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
|
||||
fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) {
|
||||
if header.has_qualifiers() {
|
||||
self.err_handler().emit_err(errors::FnQualifierInExtern {
|
||||
self.dcx().emit_err(errors::FnQualifierInExtern {
|
||||
span: ident.span,
|
||||
block: self.current_extern_span(),
|
||||
sugg_span: span.until(ident.span.shrink_to_lo()),
|
||||
@ -475,7 +466,7 @@ impl<'a> AstValidator<'a> {
|
||||
/// An item in `extern { ... }` cannot use non-ascii identifier.
|
||||
fn check_foreign_item_ascii_only(&self, ident: Ident) {
|
||||
if !ident.as_str().is_ascii() {
|
||||
self.err_handler().emit_err(errors::ExternItemAscii {
|
||||
self.dcx().emit_err(errors::ExternItemAscii {
|
||||
span: ident.span,
|
||||
block: self.current_extern_span(),
|
||||
});
|
||||
@ -504,7 +495,7 @@ impl<'a> AstValidator<'a> {
|
||||
if let Const::Yes(const_span) = header.constness {
|
||||
let mut spans = variadic_spans.clone();
|
||||
spans.push(const_span);
|
||||
self.err_handler().emit_err(errors::ConstAndCVariadic {
|
||||
self.dcx().emit_err(errors::ConstAndCVariadic {
|
||||
spans,
|
||||
const_span,
|
||||
variadic_spans: variadic_spans.clone(),
|
||||
@ -526,14 +517,14 @@ impl<'a> AstValidator<'a> {
|
||||
_ => {}
|
||||
};
|
||||
|
||||
self.err_handler().emit_err(errors::BadCVariadic { span: variadic_spans });
|
||||
self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans });
|
||||
}
|
||||
|
||||
fn check_item_named(&self, ident: Ident, kind: &str) {
|
||||
if ident.name != kw::Underscore {
|
||||
return;
|
||||
}
|
||||
self.err_handler().emit_err(errors::ItemUnderscore { span: ident.span, kind });
|
||||
self.dcx().emit_err(errors::ItemUnderscore { span: ident.span, kind });
|
||||
}
|
||||
|
||||
fn check_nomangle_item_asciionly(&self, ident: Ident, item_span: Span) {
|
||||
@ -624,14 +615,14 @@ impl<'a> AstValidator<'a> {
|
||||
let args_len = arg_spans.len();
|
||||
let constraint_len = constraint_spans.len();
|
||||
// ...and then error:
|
||||
self.err_handler().emit_err(errors::ArgsBeforeConstraint {
|
||||
self.dcx().emit_err(errors::ArgsBeforeConstraint {
|
||||
arg_spans: arg_spans.clone(),
|
||||
constraints: constraint_spans[0],
|
||||
args: *arg_spans.iter().last().unwrap(),
|
||||
data: data.span,
|
||||
constraint_spans: errors::EmptyLabelManySpans(constraint_spans),
|
||||
arg_spans2: errors::EmptyLabelManySpans(arg_spans),
|
||||
suggestion: self.correct_generic_order_suggestion(&data),
|
||||
suggestion: self.correct_generic_order_suggestion(data),
|
||||
constraint_len,
|
||||
args_len,
|
||||
});
|
||||
@ -676,7 +667,7 @@ impl<'a> AstValidator<'a> {
|
||||
}
|
||||
|
||||
if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) {
|
||||
self.err_handler().emit_err(errors::AtLeastOneTrait { span: ty.span });
|
||||
self.dcx().emit_err(errors::AtLeastOneTrait { span: ty.span });
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -706,7 +697,7 @@ impl<'a> AstValidator<'a> {
|
||||
/// Checks that generic parameters are in the correct order,
|
||||
/// which is lifetimes, then types and then consts. (`<'a, T, const N: usize>`)
|
||||
fn validate_generic_param_order(
|
||||
handler: &rustc_errors::Handler,
|
||||
dcx: &rustc_errors::DiagCtxt,
|
||||
generics: &[GenericParam],
|
||||
span: Span,
|
||||
) {
|
||||
@ -747,7 +738,7 @@ fn validate_generic_param_order(
|
||||
|
||||
if !bounds.is_empty() {
|
||||
ordered_params += ": ";
|
||||
ordered_params += &pprust::bounds_to_string(&bounds);
|
||||
ordered_params += &pprust::bounds_to_string(bounds);
|
||||
}
|
||||
|
||||
match kind {
|
||||
@ -769,7 +760,7 @@ fn validate_generic_param_order(
|
||||
ordered_params += ">";
|
||||
|
||||
for (param_ord, (max_param, spans)) in &out_of_order {
|
||||
handler.emit_err(errors::OutOfOrderParams {
|
||||
dcx.emit_err(errors::OutOfOrderParams {
|
||||
spans: spans.clone(),
|
||||
sugg_span: span,
|
||||
param_ord,
|
||||
@ -832,7 +823,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
errors::VisibilityNotPermittedNote::TraitImpl,
|
||||
);
|
||||
if let TyKind::Err = self_ty.kind {
|
||||
this.err_handler().emit_err(errors::ObsoleteAuto { span: item.span });
|
||||
this.dcx().emit_err(errors::ObsoleteAuto { span: item.span });
|
||||
}
|
||||
if let (&Unsafe::Yes(span), &ImplPolarity::Negative(sp)) = (unsafety, polarity)
|
||||
{
|
||||
@ -845,11 +836,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
|
||||
this.visit_vis(&item.vis);
|
||||
this.visit_ident(item.ident);
|
||||
if let Const::Yes(_) = constness {
|
||||
this.with_tilde_const_allowed(|this| this.visit_generics(generics));
|
||||
} else {
|
||||
this.visit_generics(generics);
|
||||
}
|
||||
let disallowed = matches!(constness, Const::No)
|
||||
.then(|| DisallowTildeConstContext::TraitImpl(item.span));
|
||||
this.with_tilde_const(disallowed, |this| this.visit_generics(generics));
|
||||
this.visit_trait_ref(t);
|
||||
this.visit_ty(self_ty);
|
||||
|
||||
@ -863,10 +852,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
polarity,
|
||||
defaultness,
|
||||
constness,
|
||||
generics: _,
|
||||
generics,
|
||||
of_trait: None,
|
||||
self_ty,
|
||||
items: _,
|
||||
items,
|
||||
}) => {
|
||||
let error =
|
||||
|annotation_span, annotation, only_trait: bool| errors::InherentImplCannot {
|
||||
@ -882,7 +871,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
errors::VisibilityNotPermittedNote::IndividualImplItems,
|
||||
);
|
||||
if let &Unsafe::Yes(span) = unsafety {
|
||||
self.err_handler().emit_err(errors::InherentImplCannotUnsafe {
|
||||
self.dcx().emit_err(errors::InherentImplCannotUnsafe {
|
||||
span: self_ty.span,
|
||||
annotation_span: span,
|
||||
annotation: "unsafe",
|
||||
@ -890,14 +879,24 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
});
|
||||
}
|
||||
if let &ImplPolarity::Negative(span) = polarity {
|
||||
self.err_handler().emit_err(error(span, "negative", false));
|
||||
self.dcx().emit_err(error(span, "negative", false));
|
||||
}
|
||||
if let &Defaultness::Default(def_span) = defaultness {
|
||||
self.err_handler().emit_err(error(def_span, "`default`", true));
|
||||
self.dcx().emit_err(error(def_span, "`default`", true));
|
||||
}
|
||||
if let &Const::Yes(span) = constness {
|
||||
self.err_handler().emit_err(error(span, "`const`", true));
|
||||
self.dcx().emit_err(error(span, "`const`", true));
|
||||
}
|
||||
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.with_tilde_const(Some(DisallowTildeConstContext::Impl(item.span)), |this| {
|
||||
this.visit_generics(generics)
|
||||
});
|
||||
self.visit_ty(self_ty);
|
||||
walk_list!(self, visit_assoc_item, items, AssocCtxt::Impl);
|
||||
walk_list!(self, visit_attribute, &item.attrs);
|
||||
return; // Avoid visiting again.
|
||||
}
|
||||
ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => {
|
||||
self.check_defaultness(item.span, *defaultness);
|
||||
@ -940,7 +939,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
errors::VisibilityNotPermittedNote::IndividualForeignItems,
|
||||
);
|
||||
if let &Unsafe::Yes(span) = unsafety {
|
||||
self.err_handler().emit_err(errors::UnsafeItem { span, kind: "extern block" });
|
||||
self.dcx().emit_err(errors::UnsafeItem { span, kind: "extern block" });
|
||||
}
|
||||
if abi.is_none() {
|
||||
self.maybe_lint_missing_abi(item.span, item.id);
|
||||
@ -978,8 +977,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
// context for the supertraits.
|
||||
this.visit_vis(&item.vis);
|
||||
this.visit_ident(item.ident);
|
||||
this.visit_generics(generics);
|
||||
this.with_tilde_const_allowed(|this| {
|
||||
let disallowed =
|
||||
(!is_const_trait).then(|| DisallowTildeConstContext::Trait(item.span));
|
||||
this.with_tilde_const(disallowed, |this| {
|
||||
this.visit_generics(generics);
|
||||
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
|
||||
});
|
||||
walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
|
||||
@ -989,7 +990,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
ItemKind::Mod(unsafety, mod_kind) => {
|
||||
if let &Unsafe::Yes(span) = unsafety {
|
||||
self.err_handler().emit_err(errors::UnsafeItem { span, kind: "module" });
|
||||
self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
|
||||
}
|
||||
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
|
||||
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
|
||||
@ -999,16 +1000,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
ItemKind::Struct(vdata, generics) => match vdata {
|
||||
// Duplicating the `Visitor` logic allows catching all cases
|
||||
// of `Anonymous(Struct, Union)` outside of a field struct or union.
|
||||
//
|
||||
// Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
|
||||
// encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
|
||||
// it uses `visit_ty_common`, which doesn't contain that specific check.
|
||||
VariantData::Struct(fields, ..) => {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_generics(generics);
|
||||
// Permit `Anon{Struct,Union}` as field type.
|
||||
walk_list!(self, visit_struct_field_def, fields);
|
||||
walk_list!(self, visit_attribute, &item.attrs);
|
||||
return;
|
||||
@ -1017,13 +1013,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
},
|
||||
ItemKind::Union(vdata, generics) => {
|
||||
if vdata.fields().is_empty() {
|
||||
self.err_handler().emit_err(errors::FieldlessUnion { span: item.span });
|
||||
self.dcx().emit_err(errors::FieldlessUnion { span: item.span });
|
||||
}
|
||||
match vdata {
|
||||
VariantData::Struct(fields, ..) => {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_generics(generics);
|
||||
// Permit `Anon{Struct,Union}` as field type.
|
||||
walk_list!(self, visit_struct_field_def, fields);
|
||||
walk_list!(self, visit_attribute, &item.attrs);
|
||||
return;
|
||||
@ -1031,12 +1028,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => {
|
||||
ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
|
||||
self.check_defaultness(item.span, *defaultness);
|
||||
self.session.emit_err(errors::ConstWithoutBody {
|
||||
span: item.span,
|
||||
replace_span: self.ending_semi_or_hi(item.span),
|
||||
});
|
||||
if expr.is_none() {
|
||||
self.session.emit_err(errors::ConstWithoutBody {
|
||||
span: item.span,
|
||||
replace_span: self.ending_semi_or_hi(item.span),
|
||||
});
|
||||
}
|
||||
}
|
||||
ItemKind::Static(box StaticItem { expr: None, .. }) => {
|
||||
self.session.emit_err(errors::StaticWithoutBody {
|
||||
@ -1058,10 +1057,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
|
||||
if self.features.lazy_type_alias {
|
||||
if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
|
||||
self.err_handler().emit_err(err);
|
||||
self.dcx().emit_err(err);
|
||||
}
|
||||
} else if where_clauses.1.0 {
|
||||
self.err_handler().emit_err(errors::WhereClauseAfterTypeAlias {
|
||||
self.dcx().emit_err(errors::WhereClauseAfterTypeAlias {
|
||||
span: where_clauses.1.1,
|
||||
help: self.session.is_nightly_build().then_some(()),
|
||||
});
|
||||
@ -1146,14 +1145,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
|
||||
if let Some(span) = prev_param_default {
|
||||
self.err_handler().emit_err(errors::GenericDefaultTrailing { span });
|
||||
self.dcx().emit_err(errors::GenericDefaultTrailing { span });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validate_generic_param_order(self.err_handler(), &generics.params, generics.span);
|
||||
validate_generic_param_order(self.dcx(), &generics.params, generics.span);
|
||||
|
||||
for predicate in &generics.where_clause.predicates {
|
||||
if let WherePredicate::EqPredicate(predicate) = predicate {
|
||||
@ -1174,7 +1173,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
match bound {
|
||||
GenericBound::Trait(t, _) => {
|
||||
if !t.bound_generic_params.is_empty() {
|
||||
self.err_handler()
|
||||
self.dcx()
|
||||
.emit_err(errors::NestedLifetimes { span: t.span });
|
||||
}
|
||||
}
|
||||
@ -1200,39 +1199,50 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
if let GenericBound::Trait(poly, modify) = bound {
|
||||
match (ctxt, modify) {
|
||||
(BoundKind::SuperTraits, TraitBoundModifier::Maybe) => {
|
||||
self.err_handler().emit_err(errors::OptionalTraitSupertrait {
|
||||
self.dcx().emit_err(errors::OptionalTraitSupertrait {
|
||||
span: poly.span,
|
||||
path_str: pprust::path_to_string(&poly.trait_ref.path),
|
||||
});
|
||||
}
|
||||
(BoundKind::TraitObject, TraitBoundModifier::Maybe) => {
|
||||
self.err_handler().emit_err(errors::OptionalTraitObject { span: poly.span });
|
||||
self.dcx().emit_err(errors::OptionalTraitObject { span: poly.span });
|
||||
}
|
||||
(_, TraitBoundModifier::MaybeConst)
|
||||
(_, &TraitBoundModifier::MaybeConst(span))
|
||||
if let Some(reason) = &self.disallow_tilde_const =>
|
||||
{
|
||||
let reason = match reason {
|
||||
DisallowTildeConstContext::TraitObject => {
|
||||
errors::TildeConstReason::TraitObject
|
||||
}
|
||||
DisallowTildeConstContext::Fn(FnKind::Closure(..)) => {
|
||||
errors::TildeConstReason::Closure
|
||||
}
|
||||
DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => {
|
||||
errors::TildeConstReason::Function { ident: ident.span }
|
||||
}
|
||||
&DisallowTildeConstContext::Trait(span) => {
|
||||
errors::TildeConstReason::Trait { span }
|
||||
}
|
||||
&DisallowTildeConstContext::TraitImpl(span) => {
|
||||
errors::TildeConstReason::TraitImpl { span }
|
||||
}
|
||||
&DisallowTildeConstContext::Impl(span) => {
|
||||
// FIXME(effects): Consider providing a help message or even a structured
|
||||
// suggestion for moving such bounds to the assoc const fns if available.
|
||||
errors::TildeConstReason::Impl { span }
|
||||
}
|
||||
DisallowTildeConstContext::TraitObject => {
|
||||
errors::TildeConstReason::TraitObject
|
||||
}
|
||||
DisallowTildeConstContext::Item => errors::TildeConstReason::Item,
|
||||
};
|
||||
self.err_handler()
|
||||
.emit_err(errors::TildeConstDisallowed { span: bound.span(), reason });
|
||||
self.dcx().emit_err(errors::TildeConstDisallowed { span, reason });
|
||||
}
|
||||
(_, TraitBoundModifier::MaybeConstMaybe) => {
|
||||
self.err_handler().emit_err(errors::OptionalConstExclusive {
|
||||
self.dcx().emit_err(errors::OptionalConstExclusive {
|
||||
span: bound.span(),
|
||||
modifier: "?",
|
||||
});
|
||||
}
|
||||
(_, TraitBoundModifier::MaybeConstNegative) => {
|
||||
self.err_handler().emit_err(errors::OptionalConstExclusive {
|
||||
self.dcx().emit_err(errors::OptionalConstExclusive {
|
||||
span: bound.span(),
|
||||
modifier: "!",
|
||||
});
|
||||
@ -1248,7 +1258,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
{
|
||||
for arg in &args.args {
|
||||
if let ast::AngleBracketedArg::Constraint(constraint) = arg {
|
||||
self.err_handler()
|
||||
self.dcx()
|
||||
.emit_err(errors::ConstraintOnNegativeBound { span: constraint.span });
|
||||
}
|
||||
}
|
||||
@ -1267,14 +1277,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
|
||||
self.check_c_variadic_type(fk);
|
||||
|
||||
// Functions cannot both be `const async`
|
||||
// Functions cannot both be `const async` or `const gen`
|
||||
if let Some(&FnHeader {
|
||||
constness: Const::Yes(cspan),
|
||||
asyncness: Async::Yes { span: aspan, .. },
|
||||
coroutine_kind: Some(coroutine_kind),
|
||||
..
|
||||
}) = fk.header()
|
||||
{
|
||||
self.err_handler().emit_err(errors::ConstAndAsync {
|
||||
let aspan = match coroutine_kind {
|
||||
CoroutineKind::Async { span: aspan, .. }
|
||||
| CoroutineKind::Gen { span: aspan, .. }
|
||||
| CoroutineKind::AsyncGen { span: aspan, .. } => aspan,
|
||||
};
|
||||
// FIXME(gen_blocks): Report a different error for `const gen`
|
||||
self.dcx().emit_err(errors::ConstAndAsync {
|
||||
spans: vec![cspan, aspan],
|
||||
cspan,
|
||||
aspan,
|
||||
@ -1314,10 +1330,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
} else {
|
||||
match ctxt {
|
||||
FnCtxt::Foreign => {
|
||||
self.err_handler().emit_err(errors::PatternInForeign { span })
|
||||
}
|
||||
_ => self.err_handler().emit_err(errors::PatternInBodiless { span }),
|
||||
FnCtxt::Foreign => self.dcx().emit_err(errors::PatternInForeign { span }),
|
||||
_ => self.dcx().emit_err(errors::PatternInBodiless { span }),
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -1328,7 +1342,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
|| matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)) if self.in_const_trait_or_impl);
|
||||
|
||||
let disallowed = (!tilde_const_allowed).then(|| DisallowTildeConstContext::Fn(fk));
|
||||
|
||||
self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk));
|
||||
}
|
||||
|
||||
@ -1397,18 +1410,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
|
||||
match &item.kind {
|
||||
AssocItemKind::Type(box TyAlias { generics, bounds, ty, .. })
|
||||
if ctxt == AssocCtxt::Trait =>
|
||||
{
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
walk_list!(self, visit_attribute, &item.attrs);
|
||||
self.with_tilde_const_allowed(|this| {
|
||||
this.visit_generics(generics);
|
||||
walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
|
||||
});
|
||||
walk_list!(self, visit_ty, ty);
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body, .. })
|
||||
if self.in_const_trait_or_impl
|
||||
|| ctxt == AssocCtxt::Trait
|
||||
@ -1461,9 +1462,7 @@ fn deny_equality_constraints(
|
||||
id: rustc_ast::node_id::DUMMY_NODE_ID,
|
||||
ident: *ident,
|
||||
gen_args,
|
||||
kind: AssocConstraintKind::Equality {
|
||||
term: predicate.rhs_ty.clone().into(),
|
||||
},
|
||||
kind: AssocConstraintKind::Equality { term: predicate.rhs_ty.clone().into() },
|
||||
span: ident.span,
|
||||
});
|
||||
// Add `<Bar = RhsTy>` to `Foo`.
|
||||
@ -1476,11 +1475,7 @@ fn deny_equality_constraints(
|
||||
},
|
||||
empty_args => {
|
||||
*empty_args = Some(
|
||||
AngleBracketedArgs {
|
||||
span: ident.span,
|
||||
args: thin_vec![arg],
|
||||
}
|
||||
.into(),
|
||||
AngleBracketedArgs { span: ident.span, args: thin_vec![arg] }.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1535,7 +1530,7 @@ fn deny_equality_constraints(
|
||||
}
|
||||
}
|
||||
}
|
||||
this.err_handler().emit_err(err);
|
||||
this.dcx().emit_err(err);
|
||||
}
|
||||
|
||||
pub fn check_crate(
|
||||
@ -1552,7 +1547,7 @@ pub fn check_crate(
|
||||
in_const_trait_or_impl: false,
|
||||
has_proc_macro_decls: false,
|
||||
outer_impl_trait: None,
|
||||
disallow_tilde_const: None,
|
||||
disallow_tilde_const: Some(DisallowTildeConstContext::Item),
|
||||
is_impl_trait_banned: false,
|
||||
lint_buffer: lints,
|
||||
};
|
||||
|
||||
@ -551,8 +551,6 @@ pub struct TildeConstDisallowed {
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub enum TildeConstReason {
|
||||
#[note(ast_passes_trait)]
|
||||
TraitObject,
|
||||
#[note(ast_passes_closure)]
|
||||
Closure,
|
||||
#[note(ast_passes_function)]
|
||||
@ -560,6 +558,25 @@ pub enum TildeConstReason {
|
||||
#[primary_span]
|
||||
ident: Span,
|
||||
},
|
||||
#[note(ast_passes_trait)]
|
||||
Trait {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[note(ast_passes_trait_impl)]
|
||||
TraitImpl {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[note(ast_passes_impl)]
|
||||
Impl {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[note(ast_passes_object)]
|
||||
TraitObject,
|
||||
#[note(ast_passes_item)]
|
||||
Item,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
@ -746,3 +763,12 @@ pub struct AnonStructOrUnionNotAllowed {
|
||||
pub span: Span,
|
||||
pub struct_or_union: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_match_arm_with_no_body)]
|
||||
pub struct MatchArmWithNoBody {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
|
||||
pub suggestion: Span,
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ impl<'a> PostExpansionVisitor<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
match abi::is_enabled(&self.features, span, symbol_unescaped.as_str()) {
|
||||
match abi::is_enabled(self.features, span, symbol_unescaped.as_str()) {
|
||||
Ok(()) => (),
|
||||
Err(abi::AbiDisabled::Unstable { feature, explain }) => {
|
||||
feature_err_issue(
|
||||
@ -102,7 +102,7 @@ impl<'a> PostExpansionVisitor<'a> {
|
||||
}
|
||||
Err(abi::AbiDisabled::Unrecognized) => {
|
||||
if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {
|
||||
self.sess.parse_sess.span_diagnostic.delay_span_bug(
|
||||
self.sess.dcx().span_delayed_bug(
|
||||
span,
|
||||
format!(
|
||||
"unrecognized ABI not caught in lowering: {}",
|
||||
@ -182,7 +182,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
..
|
||||
}) = attr_info
|
||||
{
|
||||
gate_alt!(self, has_feature(&self.features), *name, attr.span, *descr);
|
||||
gate_alt!(self, has_feature(self.features), *name, attr.span, *descr);
|
||||
}
|
||||
// Check unstable flavors of the `#[doc]` attribute.
|
||||
if attr.has_name(sym::doc) {
|
||||
@ -300,7 +300,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
}
|
||||
|
||||
ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => {
|
||||
self.check_impl_trait(&ty, false)
|
||||
self.check_impl_trait(ty, false)
|
||||
}
|
||||
|
||||
_ => {}
|
||||
@ -556,6 +556,34 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
||||
gate_all!(generic_const_items, "generic const items are experimental");
|
||||
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
|
||||
|
||||
if !visitor.features.never_patterns {
|
||||
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||
for &span in spans {
|
||||
if span.allows_unstable(sym::never_patterns) {
|
||||
continue;
|
||||
}
|
||||
let sm = sess.source_map();
|
||||
// We gate two types of spans: the span of a `!` pattern, and the span of a
|
||||
// match arm without a body. For the latter we want to give the user a normal
|
||||
// error.
|
||||
if let Ok(snippet) = sm.span_to_snippet(span)
|
||||
&& snippet == "!"
|
||||
{
|
||||
feature_err(
|
||||
&sess.parse_sess,
|
||||
sym::never_patterns,
|
||||
span,
|
||||
"`!` patterns are experimental",
|
||||
)
|
||||
.emit();
|
||||
} else {
|
||||
let suggestion = span.shrink_to_hi();
|
||||
sess.emit_err(errors::MatchArmWithNoBody { span, suggestion });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !visitor.features.negative_bounds {
|
||||
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
|
||||
sess.emit_err(errors::NegativeBoundUnsupported { span });
|
||||
@ -627,7 +655,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
|
||||
if all_stable {
|
||||
err.sugg = Some(attr.span);
|
||||
}
|
||||
sess.parse_sess.span_diagnostic.emit_err(err);
|
||||
sess.dcx().emit_err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
//!
|
||||
//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![allow(internal_features)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_is_partitioned)]
|
||||
@ -15,13 +15,10 @@
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
||||
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
|
||||
pub mod ast_validation;
|
||||
mod errors;
|
||||
pub mod feature_gate;
|
||||
pub mod node_count;
|
||||
pub mod show_span;
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
@ -31,37 +31,37 @@ impl FromStr for Mode {
|
||||
}
|
||||
|
||||
struct ShowSpanVisitor<'a> {
|
||||
span_diagnostic: &'a rustc_errors::Handler,
|
||||
dcx: &'a rustc_errors::DiagCtxt,
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
|
||||
fn visit_expr(&mut self, e: &'a ast::Expr) {
|
||||
if let Mode::Expression = self.mode {
|
||||
self.span_diagnostic.emit_warning(errors::ShowSpan { span: e.span, msg: "expression" });
|
||||
self.dcx.emit_warning(errors::ShowSpan { span: e.span, msg: "expression" });
|
||||
}
|
||||
visit::walk_expr(self, e);
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, p: &'a ast::Pat) {
|
||||
if let Mode::Pattern = self.mode {
|
||||
self.span_diagnostic.emit_warning(errors::ShowSpan { span: p.span, msg: "pattern" });
|
||||
self.dcx.emit_warning(errors::ShowSpan { span: p.span, msg: "pattern" });
|
||||
}
|
||||
visit::walk_pat(self, p);
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, t: &'a ast::Ty) {
|
||||
if let Mode::Type = self.mode {
|
||||
self.span_diagnostic.emit_warning(errors::ShowSpan { span: t.span, msg: "type" });
|
||||
self.dcx.emit_warning(errors::ShowSpan { span: t.span, msg: "type" });
|
||||
}
|
||||
visit::walk_ty(self, t);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(span_diagnostic: &rustc_errors::Handler, mode: &str, krate: &ast::Crate) {
|
||||
pub fn run(dcx: &rustc_errors::DiagCtxt, mode: &str, krate: &ast::Crate) {
|
||||
let Ok(mode) = mode.parse() else {
|
||||
return;
|
||||
};
|
||||
let mut v = ShowSpanVisitor { span_diagnostic, mode };
|
||||
let mut v = ShowSpanVisitor { dcx, mode };
|
||||
visit::walk_crate(&mut v, krate);
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
itertools = "0.11"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
thin-vec = "0.2.12"
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(with_negative_coherence)]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
mod helpers;
|
||||
|
||||
@ -165,20 +165,20 @@ enum IndentStyle {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialEq)]
|
||||
pub struct BreakToken {
|
||||
pub(crate) struct BreakToken {
|
||||
offset: isize,
|
||||
blank_space: isize,
|
||||
pre_break: Option<char>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct BeginToken {
|
||||
pub(crate) struct BeginToken {
|
||||
indent: IndentStyle,
|
||||
breaks: Breaks,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Token {
|
||||
#[derive(PartialEq)]
|
||||
pub(crate) enum Token {
|
||||
// In practice a string token contains either a `&'static str` or a
|
||||
// `String`. `Cow` is overkill for this because we never modify the data,
|
||||
// but it's more convenient than rolling our own more specialized type.
|
||||
@ -229,7 +229,6 @@ pub struct Printer {
|
||||
last_printed: Option<Token>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BufEntry {
|
||||
token: Token,
|
||||
size: isize,
|
||||
@ -251,16 +250,16 @@ impl Printer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_token(&self) -> Option<&Token> {
|
||||
pub(crate) fn last_token(&self) -> Option<&Token> {
|
||||
self.last_token_still_buffered().or_else(|| self.last_printed.as_ref())
|
||||
}
|
||||
|
||||
pub fn last_token_still_buffered(&self) -> Option<&Token> {
|
||||
pub(crate) fn last_token_still_buffered(&self) -> Option<&Token> {
|
||||
self.buf.last().map(|last| &last.token)
|
||||
}
|
||||
|
||||
/// Be very careful with this!
|
||||
pub fn replace_last_token_still_buffered(&mut self, token: Token) {
|
||||
pub(crate) fn replace_last_token_still_buffered(&mut self, token: Token) {
|
||||
self.buf.last_mut().unwrap().token = token;
|
||||
}
|
||||
|
||||
@ -314,7 +313,7 @@ impl Printer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn offset(&mut self, offset: isize) {
|
||||
pub(crate) fn offset(&mut self, offset: isize) {
|
||||
if let Some(BufEntry { token: Token::Break(token), .. }) = &mut self.buf.last_mut() {
|
||||
token.offset += offset;
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ impl Printer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hardbreak_tok_offset(off: isize) -> Token {
|
||||
pub(crate) fn hardbreak_tok_offset(off: isize) -> Token {
|
||||
Token::Break(BreakToken {
|
||||
offset: off,
|
||||
blank_space: SIZE_INFINITY,
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
mod delimited;
|
||||
//! AST pretty printing.
|
||||
//!
|
||||
//! Note that HIR pretty printing is layered on top of this crate.
|
||||
|
||||
mod expr;
|
||||
mod item;
|
||||
|
||||
use crate::pp::Breaks::{Consistent, Inconsistent};
|
||||
use crate::pp::{self, Breaks};
|
||||
use crate::pprust::state::expr::FixupContext;
|
||||
use rustc_ast::attr::AttrIdGenerator;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
||||
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
|
||||
use rustc_ast::util::parser;
|
||||
@ -23,8 +27,6 @@ use rustc_span::{BytePos, FileName, Span, DUMMY_SP};
|
||||
use std::borrow::Cow;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
pub use self::delimited::IterDelimited;
|
||||
|
||||
pub enum MacHeader<'a> {
|
||||
Path(&'a ast::Path),
|
||||
Keyword(&'static str),
|
||||
@ -46,8 +48,7 @@ pub trait PpAnn {
|
||||
fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NoAnn;
|
||||
struct NoAnn;
|
||||
|
||||
impl PpAnn for NoAnn {}
|
||||
|
||||
@ -64,11 +65,11 @@ impl<'a> Comments<'a> {
|
||||
}
|
||||
|
||||
// FIXME: This shouldn't probably clone lmao
|
||||
pub fn next(&self) -> Option<Comment> {
|
||||
fn next(&self) -> Option<Comment> {
|
||||
self.comments.get(self.current).cloned()
|
||||
}
|
||||
|
||||
pub fn trailing_comment(
|
||||
fn trailing_comment(
|
||||
&self,
|
||||
span: rustc_span::Span,
|
||||
next_pos: Option<BytePos>,
|
||||
@ -95,7 +96,7 @@ pub struct State<'a> {
|
||||
ann: &'a (dyn PpAnn + 'a),
|
||||
}
|
||||
|
||||
pub(crate) const INDENT_UNIT: isize = 4;
|
||||
const INDENT_UNIT: isize = 4;
|
||||
|
||||
/// Requires you to pass an input filename and reader so that
|
||||
/// it can scan the input text for comments to copy forward.
|
||||
@ -151,7 +152,7 @@ pub fn print_crate<'a>(
|
||||
/// Note: some old proc macros parse pretty-printed output, so changes here can
|
||||
/// break old code. For example:
|
||||
/// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,`
|
||||
/// - #73345: `#[allow(unused)] must be printed rather than `# [allow(unused)]
|
||||
/// - #73345: `#[allow(unused)]` must be printed rather than `# [allow(unused)]`
|
||||
///
|
||||
fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
|
||||
use token::*;
|
||||
@ -183,10 +184,10 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
|
||||
//
|
||||
// FIXME: Incorrect cases:
|
||||
// - Let: `let(a, b) = (1, 2)`
|
||||
(Tok(Token { kind: Ident(..), .. }, _), Del(_, Parenthesis, _)) => false,
|
||||
(Tok(Token { kind: Ident(..), .. }, _), Del(_, _, Parenthesis, _)) => false,
|
||||
|
||||
// `#` + `[`: `#[attr]`
|
||||
(Tok(Token { kind: Pound, .. }, _), Del(_, Bracket, _)) => false,
|
||||
(Tok(Token { kind: Pound, .. }, _), Del(_, _, Bracket, _)) => false,
|
||||
|
||||
_ => true,
|
||||
}
|
||||
@ -220,7 +221,7 @@ fn doc_comment_to_string(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn literal_to_string(lit: token::Lit) -> String {
|
||||
fn literal_to_string(lit: token::Lit) -> String {
|
||||
let token::Lit { kind, symbol, suffix } = lit;
|
||||
let mut out = match kind {
|
||||
token::Byte => format!("b'{symbol}'"),
|
||||
@ -260,11 +261,17 @@ impl std::ops::DerefMut for State<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is used for both AST and HIR pretty-printing.
|
||||
pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
|
||||
fn comments(&mut self) -> &mut Option<Comments<'a>>;
|
||||
fn print_ident(&mut self, ident: Ident);
|
||||
fn ann_post(&mut self, ident: Ident);
|
||||
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
|
||||
|
||||
fn print_ident(&mut self, ident: Ident) {
|
||||
self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
|
||||
self.ann_post(ident)
|
||||
}
|
||||
|
||||
fn strsep<T, F>(
|
||||
&mut self,
|
||||
sep: &'static str,
|
||||
@ -401,15 +408,6 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
}
|
||||
}
|
||||
|
||||
fn print_meta_item_lit(&mut self, lit: &ast::MetaItemLit) {
|
||||
self.print_token_literal(lit.as_token_lit(), lit.span)
|
||||
}
|
||||
|
||||
fn print_token_literal(&mut self, token_lit: token::Lit, span: Span) {
|
||||
self.maybe_print_comment(span.lo());
|
||||
self.word(token_lit.to_string())
|
||||
}
|
||||
|
||||
fn print_string(&mut self, st: &str, style: ast::StrStyle) {
|
||||
let st = match style {
|
||||
ast::StrStyle::Cooked => format!("\"{}\"", st.escape_debug()),
|
||||
@ -420,30 +418,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
self.word(st)
|
||||
}
|
||||
|
||||
fn print_symbol(&mut self, sym: Symbol, style: ast::StrStyle) {
|
||||
self.print_string(sym.as_str(), style);
|
||||
}
|
||||
|
||||
fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
|
||||
}
|
||||
|
||||
fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
|
||||
}
|
||||
|
||||
fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
|
||||
}
|
||||
|
||||
fn print_inner_attributes_inline(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
|
||||
}
|
||||
|
||||
fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
|
||||
}
|
||||
|
||||
fn print_either_attributes(
|
||||
&mut self,
|
||||
attrs: &[ast::Attribute],
|
||||
@ -467,10 +449,6 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
printed
|
||||
}
|
||||
|
||||
fn print_attribute(&mut self, attr: &ast::Attribute) {
|
||||
self.print_attribute_inline(attr, false)
|
||||
}
|
||||
|
||||
fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
|
||||
if !is_inline {
|
||||
self.hardbreak_if_not_bol();
|
||||
@ -525,33 +503,6 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
self.end();
|
||||
}
|
||||
|
||||
fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
|
||||
match item {
|
||||
ast::NestedMetaItem::MetaItem(mi) => self.print_meta_item(mi),
|
||||
ast::NestedMetaItem::Lit(lit) => self.print_meta_item_lit(lit),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_meta_item(&mut self, item: &ast::MetaItem) {
|
||||
self.ibox(INDENT_UNIT);
|
||||
match &item.kind {
|
||||
ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
|
||||
ast::MetaItemKind::NameValue(value) => {
|
||||
self.print_path(&item.path, false, 0);
|
||||
self.space();
|
||||
self.word_space("=");
|
||||
self.print_meta_item_lit(value);
|
||||
}
|
||||
ast::MetaItemKind::List(items) => {
|
||||
self.print_path(&item.path, false, 0);
|
||||
self.popen();
|
||||
self.commasep(Consistent, items, |s, i| s.print_meta_list_item(i));
|
||||
self.pclose();
|
||||
}
|
||||
}
|
||||
self.end();
|
||||
}
|
||||
|
||||
/// This doesn't deserve to be called "pretty" printing, but it should be
|
||||
/// meaning-preserving. A quick hack that might help would be to look at the
|
||||
/// spans embedded in the TTs to decide where to put spaces and newlines.
|
||||
@ -559,16 +510,17 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
/// appropriate macro, transcribe back into the grammar we just parsed from,
|
||||
/// and then pretty-print the resulting AST nodes (so, e.g., we print
|
||||
/// expression arguments as expressions). It can be done! I think.
|
||||
fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) {
|
||||
fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) -> Spacing {
|
||||
match tt {
|
||||
TokenTree::Token(token, _) => {
|
||||
TokenTree::Token(token, spacing) => {
|
||||
let token_str = self.token_to_string_ext(token, convert_dollar_crate);
|
||||
self.word(token_str);
|
||||
if let token::DocComment(..) = token.kind {
|
||||
self.hardbreak()
|
||||
}
|
||||
*spacing
|
||||
}
|
||||
TokenTree::Delimited(dspan, delim, tts) => {
|
||||
TokenTree::Delimited(dspan, spacing, delim, tts) => {
|
||||
self.print_mac_common(
|
||||
None,
|
||||
false,
|
||||
@ -578,6 +530,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
convert_dollar_crate,
|
||||
dspan.entire(),
|
||||
);
|
||||
spacing.close
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -585,9 +538,20 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) {
|
||||
let mut iter = tts.trees().peekable();
|
||||
while let Some(tt) = iter.next() {
|
||||
self.print_tt(tt, convert_dollar_crate);
|
||||
let spacing = self.print_tt(tt, convert_dollar_crate);
|
||||
if let Some(next) = iter.peek() {
|
||||
if space_between(tt, next) {
|
||||
// Should we print a space after `tt`? There are two guiding
|
||||
// factors.
|
||||
// - `spacing` is the more important and accurate one. Most
|
||||
// tokens have good spacing information, and
|
||||
// `Joint`/`JointHidden` get used a lot.
|
||||
// - `space_between` is the backup. Code produced by proc
|
||||
// macros has worse spacing information, with no
|
||||
// `JointHidden` usage and too much `Alone` usage, which
|
||||
// would result in over-spaced output such as
|
||||
// `( x () , y . z )`. `space_between` avoids some of the
|
||||
// excess whitespace.
|
||||
if spacing == Spacing::Alone && space_between(tt, next) {
|
||||
self.space();
|
||||
}
|
||||
}
|
||||
@ -825,7 +789,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
}
|
||||
token::Eof => "<eof>".into(),
|
||||
|
||||
token::Interpolated(ref nt) => self.nonterminal_to_string(nt).into(),
|
||||
token::Interpolated(ref nt) => self.nonterminal_to_string(&nt.0).into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -843,37 +807,18 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
Self::to_string(|s| s.print_type(ty))
|
||||
}
|
||||
|
||||
fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
|
||||
Self::to_string(|s| s.print_type_bounds(bounds))
|
||||
}
|
||||
|
||||
fn where_bound_predicate_to_string(
|
||||
&self,
|
||||
where_bound_predicate: &ast::WhereBoundPredicate,
|
||||
) -> String {
|
||||
Self::to_string(|s| s.print_where_bound_predicate(where_bound_predicate))
|
||||
}
|
||||
|
||||
fn pat_to_string(&self, pat: &ast::Pat) -> String {
|
||||
Self::to_string(|s| s.print_pat(pat))
|
||||
}
|
||||
|
||||
fn expr_to_string(&self, e: &ast::Expr) -> String {
|
||||
Self::to_string(|s| s.print_expr(e))
|
||||
Self::to_string(|s| s.print_expr(e, FixupContext::default()))
|
||||
}
|
||||
|
||||
fn meta_item_lit_to_string(&self, lit: &ast::MetaItemLit) -> String {
|
||||
Self::to_string(|s| s.print_meta_item_lit(lit))
|
||||
}
|
||||
|
||||
fn tt_to_string(&self, tt: &TokenTree) -> String {
|
||||
Self::to_string(|s| s.print_tt(tt, false))
|
||||
}
|
||||
|
||||
fn tts_to_string(&self, tokens: &TokenStream) -> String {
|
||||
Self::to_string(|s| s.print_tts(tokens, false))
|
||||
}
|
||||
|
||||
fn stmt_to_string(&self, stmt: &ast::Stmt) -> String {
|
||||
Self::to_string(|s| s.print_stmt(stmt))
|
||||
}
|
||||
@ -882,26 +827,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
Self::to_string(|s| s.print_item(i))
|
||||
}
|
||||
|
||||
fn assoc_item_to_string(&self, i: &ast::AssocItem) -> String {
|
||||
Self::to_string(|s| s.print_assoc_item(i))
|
||||
}
|
||||
|
||||
fn foreign_item_to_string(&self, i: &ast::ForeignItem) -> String {
|
||||
Self::to_string(|s| s.print_foreign_item(i))
|
||||
}
|
||||
|
||||
fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String {
|
||||
Self::to_string(|s| s.print_generic_params(generic_params))
|
||||
}
|
||||
|
||||
fn path_to_string(&self, p: &ast::Path) -> String {
|
||||
Self::to_string(|s| s.print_path(p, false, 0))
|
||||
}
|
||||
|
||||
fn path_segment_to_string(&self, p: &ast::PathSegment) -> String {
|
||||
Self::to_string(|s| s.print_path_segment(p, false))
|
||||
}
|
||||
|
||||
fn vis_to_string(&self, v: &ast::Visibility) -> String {
|
||||
Self::to_string(|s| s.print_visibility(v))
|
||||
}
|
||||
@ -916,22 +845,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
})
|
||||
}
|
||||
|
||||
fn meta_list_item_to_string(&self, li: &ast::NestedMetaItem) -> String {
|
||||
Self::to_string(|s| s.print_meta_list_item(li))
|
||||
}
|
||||
|
||||
fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String {
|
||||
Self::to_string(|s| s.print_attr_item(ai, ai.path.span))
|
||||
}
|
||||
|
||||
fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
|
||||
Self::to_string(|s| s.print_attribute(attr))
|
||||
}
|
||||
|
||||
fn param_to_string(&self, arg: &ast::Param) -> String {
|
||||
Self::to_string(|s| s.print_param(arg, false))
|
||||
}
|
||||
|
||||
fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
|
||||
let mut printer = State::new();
|
||||
f(&mut printer);
|
||||
@ -944,9 +861,8 @@ impl<'a> PrintState<'a> for State<'a> {
|
||||
&mut self.comments
|
||||
}
|
||||
|
||||
fn print_ident(&mut self, ident: Ident) {
|
||||
self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
|
||||
self.ann.post(self, AnnNode::Ident(&ident))
|
||||
fn ann_post(&mut self, ident: Ident) {
|
||||
self.ann.post(self, AnnNode::Ident(&ident));
|
||||
}
|
||||
|
||||
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool) {
|
||||
@ -979,13 +895,8 @@ impl<'a> State<'a> {
|
||||
State { s: pp::Printer::new(), comments: None, ann: &NoAnn }
|
||||
}
|
||||
|
||||
pub(crate) fn commasep_cmnt<T, F, G>(
|
||||
&mut self,
|
||||
b: Breaks,
|
||||
elts: &[T],
|
||||
mut op: F,
|
||||
mut get_span: G,
|
||||
) where
|
||||
fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
|
||||
where
|
||||
F: FnMut(&mut State<'_>, &T),
|
||||
G: FnMut(&T) -> rustc_span::Span,
|
||||
{
|
||||
@ -1005,8 +916,8 @@ impl<'a> State<'a> {
|
||||
self.end();
|
||||
}
|
||||
|
||||
pub(crate) fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
|
||||
self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span)
|
||||
fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) {
|
||||
self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e, FixupContext::default()), |e| e.span)
|
||||
}
|
||||
|
||||
pub fn print_opt_lifetime(&mut self, lifetime: &Option<ast::Lifetime>) {
|
||||
@ -1043,7 +954,7 @@ impl<'a> State<'a> {
|
||||
match generic_arg {
|
||||
GenericArg::Lifetime(lt) => self.print_lifetime(*lt),
|
||||
GenericArg::Type(ty) => self.print_type(ty),
|
||||
GenericArg::Const(ct) => self.print_expr(&ct.value),
|
||||
GenericArg::Const(ct) => self.print_expr(&ct.value, FixupContext::default()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1078,11 +989,11 @@ impl<'a> State<'a> {
|
||||
}
|
||||
ast::TyKind::AnonStruct(fields) => {
|
||||
self.head("struct");
|
||||
self.print_record_struct_body(&fields, ty.span);
|
||||
self.print_record_struct_body(fields, ty.span);
|
||||
}
|
||||
ast::TyKind::AnonUnion(fields) => {
|
||||
self.head("union");
|
||||
self.print_record_struct_body(&fields, ty.span);
|
||||
self.print_record_struct_body(fields, ty.span);
|
||||
}
|
||||
ast::TyKind::Paren(typ) => {
|
||||
self.popen();
|
||||
@ -1110,12 +1021,12 @@ impl<'a> State<'a> {
|
||||
self.word("[");
|
||||
self.print_type(ty);
|
||||
self.word("; ");
|
||||
self.print_expr(&length.value);
|
||||
self.print_expr(&length.value, FixupContext::default());
|
||||
self.word("]");
|
||||
}
|
||||
ast::TyKind::Typeof(e) => {
|
||||
self.word("typeof(");
|
||||
self.print_expr(&e.value);
|
||||
self.print_expr(&e.value, FixupContext::default());
|
||||
self.word(")");
|
||||
}
|
||||
ast::TyKind::Infer => {
|
||||
@ -1156,7 +1067,7 @@ impl<'a> State<'a> {
|
||||
self.print_trait_ref(&t.trait_ref)
|
||||
}
|
||||
|
||||
pub(crate) fn print_stmt(&mut self, st: &ast::Stmt) {
|
||||
fn print_stmt(&mut self, st: &ast::Stmt) {
|
||||
self.maybe_print_comment(st.span.lo());
|
||||
match &st.kind {
|
||||
ast::StmtKind::Local(loc) => {
|
||||
@ -1171,7 +1082,7 @@ impl<'a> State<'a> {
|
||||
if let Some((init, els)) = loc.kind.init_else_opt() {
|
||||
self.nbsp();
|
||||
self.word_space("=");
|
||||
self.print_expr(init);
|
||||
self.print_expr(init, FixupContext::default());
|
||||
if let Some(els) = els {
|
||||
self.cbox(INDENT_UNIT);
|
||||
self.ibox(INDENT_UNIT);
|
||||
@ -1185,14 +1096,14 @@ impl<'a> State<'a> {
|
||||
ast::StmtKind::Item(item) => self.print_item(item),
|
||||
ast::StmtKind::Expr(expr) => {
|
||||
self.space_if_not_bol();
|
||||
self.print_expr_outer_attr_style(expr, false);
|
||||
self.print_expr_outer_attr_style(expr, false, FixupContext::default());
|
||||
if classify::expr_requires_semi_to_be_stmt(expr) {
|
||||
self.word(";");
|
||||
}
|
||||
}
|
||||
ast::StmtKind::Semi(expr) => {
|
||||
self.space_if_not_bol();
|
||||
self.print_expr_outer_attr_style(expr, false);
|
||||
self.print_expr_outer_attr_style(expr, false, FixupContext::default());
|
||||
self.word(";");
|
||||
}
|
||||
ast::StmtKind::Empty => {
|
||||
@ -1211,19 +1122,19 @@ impl<'a> State<'a> {
|
||||
self.maybe_print_trailing_comment(st.span, None)
|
||||
}
|
||||
|
||||
pub(crate) fn print_block(&mut self, blk: &ast::Block) {
|
||||
fn print_block(&mut self, blk: &ast::Block) {
|
||||
self.print_block_with_attrs(blk, &[])
|
||||
}
|
||||
|
||||
pub(crate) fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
|
||||
fn print_block_unclosed_indent(&mut self, blk: &ast::Block) {
|
||||
self.print_block_maybe_unclosed(blk, &[], false)
|
||||
}
|
||||
|
||||
pub(crate) fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
|
||||
fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) {
|
||||
self.print_block_maybe_unclosed(blk, attrs, true)
|
||||
}
|
||||
|
||||
pub(crate) fn print_block_maybe_unclosed(
|
||||
fn print_block_maybe_unclosed(
|
||||
&mut self,
|
||||
blk: &ast::Block,
|
||||
attrs: &[ast::Attribute],
|
||||
@ -1244,7 +1155,7 @@ impl<'a> State<'a> {
|
||||
ast::StmtKind::Expr(expr) if i == blk.stmts.len() - 1 => {
|
||||
self.maybe_print_comment(st.span.lo());
|
||||
self.space_if_not_bol();
|
||||
self.print_expr_outer_attr_style(expr, false);
|
||||
self.print_expr_outer_attr_style(expr, false, FixupContext::default());
|
||||
self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi()));
|
||||
}
|
||||
_ => self.print_stmt(st),
|
||||
@ -1257,16 +1168,44 @@ impl<'a> State<'a> {
|
||||
}
|
||||
|
||||
/// Print a `let pat = expr` expression.
|
||||
pub(crate) fn print_let(&mut self, pat: &ast::Pat, expr: &ast::Expr) {
|
||||
///
|
||||
/// Parentheses are inserted surrounding `expr` if a round-trip through the
|
||||
/// parser would otherwise work out the wrong way in a condition position.
|
||||
///
|
||||
/// For example each of the following would mean the wrong thing without
|
||||
/// parentheses.
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// if let _ = (Struct {}) {}
|
||||
///
|
||||
/// if let _ = (true && false) {}
|
||||
/// ```
|
||||
///
|
||||
/// In a match guard, the second case still requires parens, but the first
|
||||
/// case no longer does because anything until `=>` is considered part of
|
||||
/// the match guard expression. Parsing of the expression is not terminated
|
||||
/// by `{` in that position.
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// match () {
|
||||
/// () if let _ = Struct {} => {}
|
||||
/// () if let _ = (true && false) => {}
|
||||
/// }
|
||||
/// ```
|
||||
fn print_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, fixup: FixupContext) {
|
||||
self.word("let ");
|
||||
self.print_pat(pat);
|
||||
self.space();
|
||||
self.word_space("=");
|
||||
let npals = || parser::needs_par_as_let_scrutinee(expr.precedence().order());
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals())
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
fixup.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr)
|
||||
|| parser::needs_par_as_let_scrutinee(expr.precedence().order()),
|
||||
FixupContext::default(),
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn print_mac(&mut self, m: &ast::MacCall) {
|
||||
fn print_mac(&mut self, m: &ast::MacCall) {
|
||||
self.print_mac_common(
|
||||
Some(MacHeader::Path(&m.path)),
|
||||
true,
|
||||
@ -1310,7 +1249,7 @@ impl<'a> State<'a> {
|
||||
print_reg_or_class(s, reg);
|
||||
s.pclose();
|
||||
s.space();
|
||||
s.print_expr(expr);
|
||||
s.print_expr(expr, FixupContext::default());
|
||||
}
|
||||
InlineAsmOperand::Out { reg, late, expr } => {
|
||||
s.word(if *late { "lateout" } else { "out" });
|
||||
@ -1319,7 +1258,7 @@ impl<'a> State<'a> {
|
||||
s.pclose();
|
||||
s.space();
|
||||
match expr {
|
||||
Some(expr) => s.print_expr(expr),
|
||||
Some(expr) => s.print_expr(expr, FixupContext::default()),
|
||||
None => s.word("_"),
|
||||
}
|
||||
}
|
||||
@ -1329,7 +1268,7 @@ impl<'a> State<'a> {
|
||||
print_reg_or_class(s, reg);
|
||||
s.pclose();
|
||||
s.space();
|
||||
s.print_expr(expr);
|
||||
s.print_expr(expr, FixupContext::default());
|
||||
}
|
||||
InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
|
||||
s.word(if *late { "inlateout" } else { "inout" });
|
||||
@ -1337,18 +1276,18 @@ impl<'a> State<'a> {
|
||||
print_reg_or_class(s, reg);
|
||||
s.pclose();
|
||||
s.space();
|
||||
s.print_expr(in_expr);
|
||||
s.print_expr(in_expr, FixupContext::default());
|
||||
s.space();
|
||||
s.word_space("=>");
|
||||
match out_expr {
|
||||
Some(out_expr) => s.print_expr(out_expr),
|
||||
Some(out_expr) => s.print_expr(out_expr, FixupContext::default()),
|
||||
None => s.word("_"),
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::Const { anon_const } => {
|
||||
s.word("const");
|
||||
s.space();
|
||||
s.print_expr(&anon_const.value);
|
||||
s.print_expr(&anon_const.value, FixupContext::default());
|
||||
}
|
||||
InlineAsmOperand::Sym { sym } => {
|
||||
s.word("sym");
|
||||
@ -1407,7 +1346,7 @@ impl<'a> State<'a> {
|
||||
self.pclose();
|
||||
}
|
||||
|
||||
pub(crate) fn print_local_decl(&mut self, loc: &ast::Local) {
|
||||
fn print_local_decl(&mut self, loc: &ast::Local) {
|
||||
self.print_pat(&loc.pat);
|
||||
if let Some(ty) = &loc.ty {
|
||||
self.word_space(":");
|
||||
@ -1415,7 +1354,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_name(&mut self, name: Symbol) {
|
||||
fn print_name(&mut self, name: Symbol) {
|
||||
self.word(name.to_string());
|
||||
self.ann.post(self, AnnNode::Name(&name))
|
||||
}
|
||||
@ -1439,13 +1378,14 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_pat(&mut self, pat: &ast::Pat) {
|
||||
fn print_pat(&mut self, pat: &ast::Pat) {
|
||||
self.maybe_print_comment(pat.span.lo());
|
||||
self.ann.pre(self, AnnNode::Pat(pat));
|
||||
/* Pat isn't normalized, but the beauty of it
|
||||
is that it doesn't matter */
|
||||
match &pat.kind {
|
||||
PatKind::Wild => self.word("_"),
|
||||
PatKind::Never => self.word("!"),
|
||||
PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => {
|
||||
if *by_ref == ByRef::Yes {
|
||||
self.word_nbsp("ref");
|
||||
@ -1541,10 +1481,10 @@ impl<'a> State<'a> {
|
||||
self.print_pat(inner);
|
||||
}
|
||||
}
|
||||
PatKind::Lit(e) => self.print_expr(e),
|
||||
PatKind::Lit(e) => self.print_expr(e, FixupContext::default()),
|
||||
PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => {
|
||||
if let Some(e) = begin {
|
||||
self.print_expr(e);
|
||||
self.print_expr(e, FixupContext::default());
|
||||
}
|
||||
match end_kind {
|
||||
RangeEnd::Included(RangeSyntax::DotDotDot) => self.word("..."),
|
||||
@ -1552,7 +1492,7 @@ impl<'a> State<'a> {
|
||||
RangeEnd::Excluded => self.word(".."),
|
||||
}
|
||||
if let Some(e) = end {
|
||||
self.print_expr(e);
|
||||
self.print_expr(e, FixupContext::default());
|
||||
}
|
||||
}
|
||||
PatKind::Slice(elts) => {
|
||||
@ -1592,9 +1532,18 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_asyncness(&mut self, asyncness: ast::Async) {
|
||||
if asyncness.is_async() {
|
||||
self.word_nbsp("async");
|
||||
fn print_coroutine_kind(&mut self, coroutine_kind: ast::CoroutineKind) {
|
||||
match coroutine_kind {
|
||||
ast::CoroutineKind::Gen { .. } => {
|
||||
self.word_nbsp("gen");
|
||||
}
|
||||
ast::CoroutineKind::Async { .. } => {
|
||||
self.word_nbsp("async");
|
||||
}
|
||||
ast::CoroutineKind::AsyncGen { .. } => {
|
||||
self.word_nbsp("async");
|
||||
self.word_nbsp("gen");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1618,7 +1567,7 @@ impl<'a> State<'a> {
|
||||
TraitBoundModifier::Maybe => {
|
||||
self.word("?");
|
||||
}
|
||||
TraitBoundModifier::MaybeConst => {
|
||||
TraitBoundModifier::MaybeConst(_) => {
|
||||
self.word_space("~const");
|
||||
}
|
||||
TraitBoundModifier::MaybeConstNegative => {
|
||||
@ -1637,23 +1586,25 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
|
||||
fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
|
||||
self.print_name(lifetime.ident.name)
|
||||
}
|
||||
|
||||
pub(crate) fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) {
|
||||
fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) {
|
||||
for (i, bound) in bounds.iter().enumerate() {
|
||||
if i != 0 {
|
||||
self.word(" + ");
|
||||
}
|
||||
match bound {
|
||||
ast::GenericBound::Outlives(lt) => self.print_lifetime(*lt),
|
||||
_ => panic!(),
|
||||
_ => {
|
||||
panic!("expected a lifetime bound, found a trait bound")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
|
||||
fn print_generic_params(&mut self, generic_params: &[ast::GenericParam]) {
|
||||
if generic_params.is_empty() {
|
||||
return;
|
||||
}
|
||||
@ -1697,7 +1648,7 @@ impl<'a> State<'a> {
|
||||
if let Some(default) = default {
|
||||
s.space();
|
||||
s.word_space("=");
|
||||
s.print_expr(&default.value);
|
||||
s.print_expr(&default.value, FixupContext::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1717,12 +1668,12 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
|
||||
fn print_mt(&mut self, mt: &ast::MutTy, print_const: bool) {
|
||||
self.print_mutability(mt.mutbl, print_const);
|
||||
self.print_type(&mt.ty)
|
||||
}
|
||||
|
||||
pub(crate) fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
|
||||
fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
|
||||
self.ibox(INDENT_UNIT);
|
||||
|
||||
self.print_outer_attributes_inline(&input.attrs);
|
||||
@ -1750,7 +1701,7 @@ impl<'a> State<'a> {
|
||||
self.end();
|
||||
}
|
||||
|
||||
pub(crate) fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) {
|
||||
fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) {
|
||||
if let ast::FnRetTy::Ty(ty) = fn_ret_ty {
|
||||
self.space_if_not_bol();
|
||||
self.ibox(INDENT_UNIT);
|
||||
@ -1761,7 +1712,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_ty_fn(
|
||||
fn print_ty_fn(
|
||||
&mut self,
|
||||
ext: ast::Extern,
|
||||
unsafety: ast::Unsafe,
|
||||
@ -1785,9 +1736,9 @@ impl<'a> State<'a> {
|
||||
self.end();
|
||||
}
|
||||
|
||||
pub(crate) fn print_fn_header_info(&mut self, header: ast::FnHeader) {
|
||||
fn print_fn_header_info(&mut self, header: ast::FnHeader) {
|
||||
self.print_constness(header.constness);
|
||||
self.print_asyncness(header.asyncness);
|
||||
header.coroutine_kind.map(|coroutine_kind| self.print_coroutine_kind(coroutine_kind));
|
||||
self.print_unsafety(header.unsafety);
|
||||
|
||||
match header.ext {
|
||||
@ -1805,24 +1756,109 @@ impl<'a> State<'a> {
|
||||
self.word("fn")
|
||||
}
|
||||
|
||||
pub(crate) fn print_unsafety(&mut self, s: ast::Unsafe) {
|
||||
fn print_unsafety(&mut self, s: ast::Unsafe) {
|
||||
match s {
|
||||
ast::Unsafe::No => {}
|
||||
ast::Unsafe::Yes(_) => self.word_nbsp("unsafe"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_constness(&mut self, s: ast::Const) {
|
||||
fn print_constness(&mut self, s: ast::Const) {
|
||||
match s {
|
||||
ast::Const::No => {}
|
||||
ast::Const::Yes(_) => self.word_nbsp("const"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_is_auto(&mut self, s: ast::IsAuto) {
|
||||
fn print_is_auto(&mut self, s: ast::IsAuto) {
|
||||
match s {
|
||||
ast::IsAuto::Yes => self.word_nbsp("auto"),
|
||||
ast::IsAuto::No => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_meta_item_lit(&mut self, lit: &ast::MetaItemLit) {
|
||||
self.print_token_literal(lit.as_token_lit(), lit.span)
|
||||
}
|
||||
|
||||
fn print_token_literal(&mut self, token_lit: token::Lit, span: Span) {
|
||||
self.maybe_print_comment(span.lo());
|
||||
self.word(token_lit.to_string())
|
||||
}
|
||||
|
||||
fn print_symbol(&mut self, sym: Symbol, style: ast::StrStyle) {
|
||||
self.print_string(sym.as_str(), style);
|
||||
}
|
||||
|
||||
fn print_inner_attributes_no_trailing_hardbreak(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
|
||||
}
|
||||
|
||||
fn print_outer_attributes_inline(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
|
||||
}
|
||||
|
||||
fn print_attribute(&mut self, attr: &ast::Attribute) {
|
||||
self.print_attribute_inline(attr, false)
|
||||
}
|
||||
|
||||
fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
|
||||
match item {
|
||||
ast::NestedMetaItem::MetaItem(mi) => self.print_meta_item(mi),
|
||||
ast::NestedMetaItem::Lit(lit) => self.print_meta_item_lit(lit),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_meta_item(&mut self, item: &ast::MetaItem) {
|
||||
self.ibox(INDENT_UNIT);
|
||||
match &item.kind {
|
||||
ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
|
||||
ast::MetaItemKind::NameValue(value) => {
|
||||
self.print_path(&item.path, false, 0);
|
||||
self.space();
|
||||
self.word_space("=");
|
||||
self.print_meta_item_lit(value);
|
||||
}
|
||||
ast::MetaItemKind::List(items) => {
|
||||
self.print_path(&item.path, false, 0);
|
||||
self.popen();
|
||||
self.commasep(Consistent, items, |s, i| s.print_meta_list_item(i));
|
||||
self.pclose();
|
||||
}
|
||||
}
|
||||
self.end();
|
||||
}
|
||||
|
||||
pub(crate) fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
|
||||
Self::to_string(|s| s.print_type_bounds(bounds))
|
||||
}
|
||||
|
||||
pub(crate) fn where_bound_predicate_to_string(
|
||||
&self,
|
||||
where_bound_predicate: &ast::WhereBoundPredicate,
|
||||
) -> String {
|
||||
Self::to_string(|s| s.print_where_bound_predicate(where_bound_predicate))
|
||||
}
|
||||
|
||||
pub(crate) fn tt_to_string(&self, tt: &TokenTree) -> String {
|
||||
Self::to_string(|s| {
|
||||
s.print_tt(tt, false);
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn tts_to_string(&self, tokens: &TokenStream) -> String {
|
||||
Self::to_string(|s| s.print_tts(tokens, false))
|
||||
}
|
||||
|
||||
pub(crate) fn path_segment_to_string(&self, p: &ast::PathSegment) -> String {
|
||||
Self::to_string(|s| s.print_path_segment(p, false))
|
||||
}
|
||||
|
||||
pub(crate) fn meta_list_item_to_string(&self, li: &ast::NestedMetaItem) -> String {
|
||||
Self::to_string(|s| s.print_meta_list_item(li))
|
||||
}
|
||||
|
||||
pub(crate) fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
|
||||
Self::to_string(|s| s.print_attribute(attr))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
use std::iter::Peekable;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct Delimited<I: Iterator> {
|
||||
is_first: bool,
|
||||
iter: Peekable<I>,
|
||||
}
|
||||
|
||||
pub trait IterDelimited: Iterator + Sized {
|
||||
fn delimited(self) -> Delimited<Self> {
|
||||
Delimited { is_first: true, iter: self.peekable() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> IterDelimited for I {}
|
||||
|
||||
pub struct IteratorItem<T> {
|
||||
value: T,
|
||||
pub is_first: bool,
|
||||
pub is_last: bool,
|
||||
}
|
||||
|
||||
impl<I: Iterator> Iterator for Delimited<I> {
|
||||
type Item = IteratorItem<I::Item>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let value = self.iter.next()?;
|
||||
let is_first = mem::replace(&mut self.is_first, false);
|
||||
let is_last = self.iter.peek().is_none();
|
||||
Some(IteratorItem { value, is_first, is_last })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for IteratorItem<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::pp::Breaks::Inconsistent;
|
||||
use crate::pprust::state::{AnnNode, IterDelimited, PrintState, State, INDENT_UNIT};
|
||||
|
||||
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
|
||||
use itertools::{Itertools, Position};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::util::literal::escape_byte_str_symbol;
|
||||
@ -12,6 +12,19 @@ use rustc_ast::{
|
||||
};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct FixupContext {
|
||||
pub parenthesize_exterior_struct_lit: bool,
|
||||
}
|
||||
|
||||
/// The default amount of fixing is minimal fixing. Fixups should be turned on
|
||||
/// in a targetted fashion where needed.
|
||||
impl Default for FixupContext {
|
||||
fn default() -> Self {
|
||||
FixupContext { parenthesize_exterior_struct_lit: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
fn print_else(&mut self, els: Option<&ast::Expr>) {
|
||||
if let Some(_else) = els {
|
||||
@ -55,21 +68,22 @@ impl<'a> State<'a> {
|
||||
self.pclose()
|
||||
}
|
||||
|
||||
fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) {
|
||||
self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
|
||||
fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8, fixup: FixupContext) {
|
||||
self.print_expr_cond_paren(expr, expr.precedence().order() < prec, fixup);
|
||||
}
|
||||
|
||||
/// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
|
||||
/// `if cond { ... }`.
|
||||
fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
|
||||
let fixup = FixupContext { parenthesize_exterior_struct_lit: true };
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), fixup)
|
||||
}
|
||||
|
||||
/// Does `expr` need parentheses when printed in a condition position?
|
||||
///
|
||||
/// These cases need parens due to the parse error observed in #26461: `if return {}`
|
||||
/// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
|
||||
pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool {
|
||||
fn cond_needs_par(expr: &ast::Expr) -> bool {
|
||||
match expr.kind {
|
||||
ast::ExprKind::Break(..)
|
||||
| ast::ExprKind::Closure(..)
|
||||
@ -80,11 +94,32 @@ impl<'a> State<'a> {
|
||||
}
|
||||
|
||||
/// Prints `expr` or `(expr)` when `needs_par` holds.
|
||||
pub(super) fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) {
|
||||
pub(super) fn print_expr_cond_paren(
|
||||
&mut self,
|
||||
expr: &ast::Expr,
|
||||
needs_par: bool,
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
if needs_par {
|
||||
self.popen();
|
||||
}
|
||||
self.print_expr(expr);
|
||||
|
||||
// If we are surrounding the whole cond in parentheses, such as:
|
||||
//
|
||||
// if (return Struct {}) {}
|
||||
//
|
||||
// then there is no need for parenthesizing the individual struct
|
||||
// expressions within. On the other hand if the whole cond is not
|
||||
// parenthesized, then print_expr must parenthesize exterior struct
|
||||
// literals.
|
||||
//
|
||||
// if x == (Struct {}) {}
|
||||
//
|
||||
let fixup = FixupContext {
|
||||
parenthesize_exterior_struct_lit: fixup.parenthesize_exterior_struct_lit && !needs_par,
|
||||
};
|
||||
self.print_expr(expr, fixup);
|
||||
|
||||
if needs_par {
|
||||
self.pclose();
|
||||
}
|
||||
@ -111,7 +146,7 @@ impl<'a> State<'a> {
|
||||
self.ibox(0);
|
||||
self.print_block_with_attrs(block, attrs);
|
||||
} else {
|
||||
self.print_expr(&expr.value);
|
||||
self.print_expr(&expr.value, FixupContext::default());
|
||||
}
|
||||
self.end();
|
||||
}
|
||||
@ -119,9 +154,9 @@ impl<'a> State<'a> {
|
||||
fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) {
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.word("[");
|
||||
self.print_expr(element);
|
||||
self.print_expr(element, FixupContext::default());
|
||||
self.word_space(";");
|
||||
self.print_expr(&count.value);
|
||||
self.print_expr(&count.value, FixupContext::default());
|
||||
self.word("]");
|
||||
self.end();
|
||||
}
|
||||
@ -149,18 +184,20 @@ impl<'a> State<'a> {
|
||||
return;
|
||||
}
|
||||
self.cbox(0);
|
||||
for field in fields.iter().delimited() {
|
||||
for (pos, field) in fields.iter().with_position() {
|
||||
let is_first = matches!(pos, Position::First | Position::Only);
|
||||
let is_last = matches!(pos, Position::Last | Position::Only);
|
||||
self.maybe_print_comment(field.span.hi());
|
||||
self.print_outer_attributes(&field.attrs);
|
||||
if field.is_first {
|
||||
if is_first {
|
||||
self.space_if_not_bol();
|
||||
}
|
||||
if !field.is_shorthand {
|
||||
self.print_ident(field.ident);
|
||||
self.word_nbsp(":");
|
||||
}
|
||||
self.print_expr(&field.expr);
|
||||
if !field.is_last || has_rest {
|
||||
self.print_expr(&field.expr, FixupContext::default());
|
||||
if !is_last || has_rest {
|
||||
self.word_space(",");
|
||||
} else {
|
||||
self.trailing_comma_or_space();
|
||||
@ -172,7 +209,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
self.word("..");
|
||||
if let ast::StructRest::Base(expr) = rest {
|
||||
self.print_expr(expr);
|
||||
self.print_expr(expr, FixupContext::default());
|
||||
}
|
||||
self.space();
|
||||
}
|
||||
@ -190,13 +227,13 @@ impl<'a> State<'a> {
|
||||
self.pclose()
|
||||
}
|
||||
|
||||
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>]) {
|
||||
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
|
||||
let prec = match func.kind {
|
||||
ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
|
||||
_ => parser::PREC_POSTFIX,
|
||||
};
|
||||
|
||||
self.print_expr_maybe_paren(func, prec);
|
||||
self.print_expr_maybe_paren(func, prec, fixup);
|
||||
self.print_call_post(args)
|
||||
}
|
||||
|
||||
@ -205,8 +242,9 @@ impl<'a> State<'a> {
|
||||
segment: &ast::PathSegment,
|
||||
receiver: &ast::Expr,
|
||||
base_args: &[P<ast::Expr>],
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
self.print_expr_maybe_paren(receiver, parser::PREC_POSTFIX);
|
||||
self.print_expr_maybe_paren(receiver, parser::PREC_POSTFIX, fixup);
|
||||
self.word(".");
|
||||
self.print_ident(segment.ident);
|
||||
if let Some(args) = &segment.args {
|
||||
@ -215,7 +253,13 @@ impl<'a> State<'a> {
|
||||
self.print_call_post(base_args)
|
||||
}
|
||||
|
||||
fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) {
|
||||
fn print_expr_binary(
|
||||
&mut self,
|
||||
op: ast::BinOp,
|
||||
lhs: &ast::Expr,
|
||||
rhs: &ast::Expr,
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
let assoc_op = AssocOp::from_ast_binop(op.node);
|
||||
let prec = assoc_op.precedence() as i8;
|
||||
let fixity = assoc_op.fixity();
|
||||
@ -251,15 +295,15 @@ impl<'a> State<'a> {
|
||||
_ => left_prec,
|
||||
};
|
||||
|
||||
self.print_expr_maybe_paren(lhs, left_prec);
|
||||
self.print_expr_maybe_paren(lhs, left_prec, fixup);
|
||||
self.space();
|
||||
self.word_space(op.node.to_string());
|
||||
self.print_expr_maybe_paren(rhs, right_prec)
|
||||
self.word_space(op.node.as_str());
|
||||
self.print_expr_maybe_paren(rhs, right_prec, fixup)
|
||||
}
|
||||
|
||||
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) {
|
||||
self.word(ast::UnOp::to_string(op));
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
|
||||
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
|
||||
self.word(op.as_str());
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup)
|
||||
}
|
||||
|
||||
fn print_expr_addr_of(
|
||||
@ -267,6 +311,7 @@ impl<'a> State<'a> {
|
||||
kind: ast::BorrowKind,
|
||||
mutability: ast::Mutability,
|
||||
expr: &ast::Expr,
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
self.word("&");
|
||||
match kind {
|
||||
@ -276,14 +321,19 @@ impl<'a> State<'a> {
|
||||
self.print_mutability(mutability, true);
|
||||
}
|
||||
}
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup)
|
||||
}
|
||||
|
||||
pub fn print_expr(&mut self, expr: &ast::Expr) {
|
||||
self.print_expr_outer_attr_style(expr, true)
|
||||
pub(super) fn print_expr(&mut self, expr: &ast::Expr, fixup: FixupContext) {
|
||||
self.print_expr_outer_attr_style(expr, true, fixup)
|
||||
}
|
||||
|
||||
pub(super) fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) {
|
||||
pub(super) fn print_expr_outer_attr_style(
|
||||
&mut self,
|
||||
expr: &ast::Expr,
|
||||
is_inline: bool,
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
self.maybe_print_comment(expr.span.lo());
|
||||
|
||||
let attrs = &expr.attrs;
|
||||
@ -312,19 +362,19 @@ impl<'a> State<'a> {
|
||||
self.print_expr_tup(exprs);
|
||||
}
|
||||
ast::ExprKind::Call(func, args) => {
|
||||
self.print_expr_call(func, args);
|
||||
self.print_expr_call(func, args, fixup);
|
||||
}
|
||||
ast::ExprKind::MethodCall(box ast::MethodCall { seg, receiver, args, .. }) => {
|
||||
self.print_expr_method_call(seg, receiver, args);
|
||||
self.print_expr_method_call(seg, receiver, args, fixup);
|
||||
}
|
||||
ast::ExprKind::Binary(op, lhs, rhs) => {
|
||||
self.print_expr_binary(*op, lhs, rhs);
|
||||
self.print_expr_binary(*op, lhs, rhs, fixup);
|
||||
}
|
||||
ast::ExprKind::Unary(op, expr) => {
|
||||
self.print_expr_unary(*op, expr);
|
||||
self.print_expr_unary(*op, expr, fixup);
|
||||
}
|
||||
ast::ExprKind::AddrOf(k, m, expr) => {
|
||||
self.print_expr_addr_of(*k, *m, expr);
|
||||
self.print_expr_addr_of(*k, *m, expr, fixup);
|
||||
}
|
||||
ast::ExprKind::Lit(token_lit) => {
|
||||
self.print_token_literal(*token_lit, expr.span);
|
||||
@ -335,7 +385,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
ast::ExprKind::Cast(expr, ty) => {
|
||||
let prec = AssocOp::As.precedence() as i8;
|
||||
self.print_expr_maybe_paren(expr, prec);
|
||||
self.print_expr_maybe_paren(expr, prec, fixup);
|
||||
self.space();
|
||||
self.word_space("as");
|
||||
self.print_type(ty);
|
||||
@ -343,7 +393,7 @@ impl<'a> State<'a> {
|
||||
ast::ExprKind::Type(expr, ty) => {
|
||||
self.word("type_ascribe!(");
|
||||
self.ibox(0);
|
||||
self.print_expr(expr);
|
||||
self.print_expr(expr, FixupContext::default());
|
||||
|
||||
self.word(",");
|
||||
self.space_if_not_bol();
|
||||
@ -353,7 +403,7 @@ impl<'a> State<'a> {
|
||||
self.word(")");
|
||||
}
|
||||
ast::ExprKind::Let(pat, scrutinee, _, _) => {
|
||||
self.print_let(pat, scrutinee);
|
||||
self.print_let(pat, scrutinee, fixup);
|
||||
}
|
||||
ast::ExprKind::If(test, blk, elseopt) => self.print_if(test, blk, elseopt.as_deref()),
|
||||
ast::ExprKind::While(test, blk, opt_label) => {
|
||||
@ -411,7 +461,7 @@ impl<'a> State<'a> {
|
||||
binder,
|
||||
capture_clause,
|
||||
constness,
|
||||
asyncness,
|
||||
coroutine_kind,
|
||||
movability,
|
||||
fn_decl,
|
||||
body,
|
||||
@ -421,12 +471,12 @@ impl<'a> State<'a> {
|
||||
self.print_closure_binder(binder);
|
||||
self.print_constness(*constness);
|
||||
self.print_movability(*movability);
|
||||
self.print_asyncness(*asyncness);
|
||||
coroutine_kind.map(|coroutine_kind| self.print_coroutine_kind(coroutine_kind));
|
||||
self.print_capture_clause(*capture_clause);
|
||||
|
||||
self.print_fn_params_and_ret(fn_decl, true);
|
||||
self.space();
|
||||
self.print_expr(body);
|
||||
self.print_expr(body, FixupContext::default());
|
||||
self.end(); // need to close a box
|
||||
|
||||
// a box will be closed by print_expr, but we didn't want an overall
|
||||
@ -454,33 +504,33 @@ impl<'a> State<'a> {
|
||||
self.print_block_with_attrs(blk, attrs);
|
||||
}
|
||||
ast::ExprKind::Await(expr, _) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
|
||||
self.word(".await");
|
||||
}
|
||||
ast::ExprKind::Assign(lhs, rhs, _) => {
|
||||
let prec = AssocOp::Assign.precedence() as i8;
|
||||
self.print_expr_maybe_paren(lhs, prec + 1);
|
||||
self.print_expr_maybe_paren(lhs, prec + 1, fixup);
|
||||
self.space();
|
||||
self.word_space("=");
|
||||
self.print_expr_maybe_paren(rhs, prec);
|
||||
self.print_expr_maybe_paren(rhs, prec, fixup);
|
||||
}
|
||||
ast::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
let prec = AssocOp::Assign.precedence() as i8;
|
||||
self.print_expr_maybe_paren(lhs, prec + 1);
|
||||
self.print_expr_maybe_paren(lhs, prec + 1, fixup);
|
||||
self.space();
|
||||
self.word(op.node.to_string());
|
||||
self.word(op.node.as_str());
|
||||
self.word_space("=");
|
||||
self.print_expr_maybe_paren(rhs, prec);
|
||||
self.print_expr_maybe_paren(rhs, prec, fixup);
|
||||
}
|
||||
ast::ExprKind::Field(expr, ident) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
|
||||
self.word(".");
|
||||
self.print_ident(*ident);
|
||||
}
|
||||
ast::ExprKind::Index(expr, index, _) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
|
||||
self.word("[");
|
||||
self.print_expr(index);
|
||||
self.print_expr(index, FixupContext::default());
|
||||
self.word("]");
|
||||
}
|
||||
ast::ExprKind::Range(start, end, limits) => {
|
||||
@ -490,14 +540,14 @@ impl<'a> State<'a> {
|
||||
// a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
|
||||
let fake_prec = AssocOp::LOr.precedence() as i8;
|
||||
if let Some(e) = start {
|
||||
self.print_expr_maybe_paren(e, fake_prec);
|
||||
self.print_expr_maybe_paren(e, fake_prec, fixup);
|
||||
}
|
||||
match limits {
|
||||
ast::RangeLimits::HalfOpen => self.word(".."),
|
||||
ast::RangeLimits::Closed => self.word("..="),
|
||||
}
|
||||
if let Some(e) = end {
|
||||
self.print_expr_maybe_paren(e, fake_prec);
|
||||
self.print_expr_maybe_paren(e, fake_prec, fixup);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Underscore => self.word("_"),
|
||||
@ -511,7 +561,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
if let Some(expr) = opt_expr {
|
||||
self.space();
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP, fixup);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Continue(opt_label) => {
|
||||
@ -525,7 +575,7 @@ impl<'a> State<'a> {
|
||||
self.word("return");
|
||||
if let Some(expr) = result {
|
||||
self.word(" ");
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP, fixup);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Yeet(result) => {
|
||||
@ -534,13 +584,13 @@ impl<'a> State<'a> {
|
||||
self.word("yeet");
|
||||
if let Some(expr) = result {
|
||||
self.word(" ");
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP, fixup);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Become(result) => {
|
||||
self.word("become");
|
||||
self.word(" ");
|
||||
self.print_expr_maybe_paren(result, parser::PREC_JUMP);
|
||||
self.print_expr_maybe_paren(result, parser::PREC_JUMP, fixup);
|
||||
}
|
||||
ast::ExprKind::InlineAsm(a) => {
|
||||
// FIXME: This should have its own syntax, distinct from a macro invocation.
|
||||
@ -555,7 +605,7 @@ impl<'a> State<'a> {
|
||||
self.word(reconstruct_format_args_template_string(&fmt.template));
|
||||
for arg in fmt.arguments.all_args() {
|
||||
self.word_space(",");
|
||||
self.print_expr(&arg.expr);
|
||||
self.print_expr(&arg.expr, FixupContext::default());
|
||||
}
|
||||
self.end();
|
||||
self.pclose();
|
||||
@ -582,7 +632,7 @@ impl<'a> State<'a> {
|
||||
ast::ExprKind::MacCall(m) => self.print_mac(m),
|
||||
ast::ExprKind::Paren(e) => {
|
||||
self.popen();
|
||||
self.print_expr(e);
|
||||
self.print_expr(e, FixupContext::default());
|
||||
self.pclose();
|
||||
}
|
||||
ast::ExprKind::Yield(e) => {
|
||||
@ -590,11 +640,11 @@ impl<'a> State<'a> {
|
||||
|
||||
if let Some(expr) = e {
|
||||
self.space();
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP, fixup);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Try(e) => {
|
||||
self.print_expr_maybe_paren(e, parser::PREC_POSTFIX);
|
||||
self.print_expr_maybe_paren(e, parser::PREC_POSTFIX, fixup);
|
||||
self.word("?")
|
||||
}
|
||||
ast::ExprKind::TryBlock(blk) => {
|
||||
@ -626,31 +676,36 @@ impl<'a> State<'a> {
|
||||
self.space();
|
||||
if let Some(e) = &arm.guard {
|
||||
self.word_space("if");
|
||||
self.print_expr(e);
|
||||
self.print_expr(e, FixupContext::default());
|
||||
self.space();
|
||||
}
|
||||
self.word_space("=>");
|
||||
|
||||
match &arm.body.kind {
|
||||
ast::ExprKind::Block(blk, opt_label) => {
|
||||
if let Some(label) = opt_label {
|
||||
self.print_ident(label.ident);
|
||||
self.word_space(":");
|
||||
if let Some(body) = &arm.body {
|
||||
self.word_space("=>");
|
||||
|
||||
match &body.kind {
|
||||
ast::ExprKind::Block(blk, opt_label) => {
|
||||
if let Some(label) = opt_label {
|
||||
self.print_ident(label.ident);
|
||||
self.word_space(":");
|
||||
}
|
||||
|
||||
// The block will close the pattern's ibox.
|
||||
self.print_block_unclosed_indent(blk);
|
||||
|
||||
// If it is a user-provided unsafe block, print a comma after it.
|
||||
if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
|
||||
self.word(",");
|
||||
}
|
||||
}
|
||||
|
||||
// The block will close the pattern's ibox.
|
||||
self.print_block_unclosed_indent(blk);
|
||||
|
||||
// If it is a user-provided unsafe block, print a comma after it.
|
||||
if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
|
||||
_ => {
|
||||
self.end(); // Close the ibox for the pattern.
|
||||
self.print_expr(body, FixupContext::default());
|
||||
self.word(",");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.end(); // Close the ibox for the pattern.
|
||||
self.print_expr(&arm.body);
|
||||
self.word(",");
|
||||
}
|
||||
} else {
|
||||
self.word(",");
|
||||
}
|
||||
self.end(); // Close enclosing cbox.
|
||||
}
|
||||
@ -679,7 +734,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String {
|
||||
fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String {
|
||||
let mut template = "\"".to_string();
|
||||
for piece in pieces {
|
||||
match piece {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use crate::pp::Breaks::Inconsistent;
|
||||
use crate::pprust::state::delimited::IterDelimited;
|
||||
use crate::pprust::state::expr::FixupContext;
|
||||
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
|
||||
|
||||
use ast::StaticItem;
|
||||
use itertools::{Itertools, Position};
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::GenericBound;
|
||||
use rustc_ast::ModKind;
|
||||
@ -20,7 +21,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
|
||||
fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
|
||||
let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
|
||||
self.ann.pre(self, AnnNode::SubItem(id));
|
||||
self.hardbreak_if_not_bol();
|
||||
@ -97,7 +98,7 @@ impl<'a> State<'a> {
|
||||
self.end(); // end the head-ibox
|
||||
if let Some(body) = body {
|
||||
self.word_space("=");
|
||||
self.print_expr(body);
|
||||
self.print_expr(body, FixupContext::default());
|
||||
}
|
||||
self.print_where_clause(&generics.where_clause);
|
||||
self.word(";");
|
||||
@ -368,7 +369,7 @@ impl<'a> State<'a> {
|
||||
self.nbsp();
|
||||
if !bounds.is_empty() {
|
||||
self.word_nbsp("=");
|
||||
self.print_type_bounds(&bounds);
|
||||
self.print_type_bounds(bounds);
|
||||
}
|
||||
self.print_where_clause(&generics.where_clause);
|
||||
self.word(";");
|
||||
@ -499,7 +500,7 @@ impl<'a> State<'a> {
|
||||
self.end();
|
||||
self.end(); // Close the outer-box.
|
||||
}
|
||||
ast::VariantData::Struct(fields, ..) => {
|
||||
ast::VariantData::Struct { fields, .. } => {
|
||||
self.print_where_clause(&generics.where_clause);
|
||||
self.print_record_struct_body(fields, span);
|
||||
}
|
||||
@ -514,11 +515,11 @@ impl<'a> State<'a> {
|
||||
if let Some(d) = &v.disr_expr {
|
||||
self.space();
|
||||
self.word_space("=");
|
||||
self.print_expr(&d.value)
|
||||
self.print_expr(&d.value, FixupContext::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_assoc_item(&mut self, item: &ast::AssocItem) {
|
||||
fn print_assoc_item(&mut self, item: &ast::AssocItem) {
|
||||
let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
|
||||
self.ann.pre(self, AnnNode::SubItem(id));
|
||||
self.hardbreak_if_not_bol();
|
||||
@ -621,7 +622,7 @@ impl<'a> State<'a> {
|
||||
self.print_where_clause_parts(where_clause.has_where_token, &where_clause.predicates);
|
||||
}
|
||||
|
||||
pub(crate) fn print_where_clause_parts(
|
||||
fn print_where_clause_parts(
|
||||
&mut self,
|
||||
has_where_token: bool,
|
||||
predicates: &[ast::WherePredicate],
|
||||
@ -668,7 +669,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_where_bound_predicate(
|
||||
pub(crate) fn print_where_bound_predicate(
|
||||
&mut self,
|
||||
where_bound_predicate: &ast::WhereBoundPredicate,
|
||||
) {
|
||||
@ -712,9 +713,10 @@ impl<'a> State<'a> {
|
||||
self.word("{");
|
||||
self.zerobreak();
|
||||
self.ibox(0);
|
||||
for use_tree in items.iter().delimited() {
|
||||
for (pos, use_tree) in items.iter().with_position() {
|
||||
let is_last = matches!(pos, Position::Last | Position::Only);
|
||||
self.print_use_tree(&use_tree.0);
|
||||
if !use_tree.is_last {
|
||||
if !is_last {
|
||||
self.word(",");
|
||||
if let ast::UseTreeKind::Nested(_) = use_tree.0.kind {
|
||||
self.hardbreak();
|
||||
|
||||
@ -552,7 +552,7 @@ pub fn cfg_matches(
|
||||
fn try_gate_cfg(name: Symbol, span: Span, sess: &ParseSess, features: Option<&Features>) {
|
||||
let gate = find_gated_cfg(|sym| sym == name);
|
||||
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
|
||||
gate_cfg(&gated_cfg, span, sess, feats);
|
||||
gate_cfg(gated_cfg, span, sess, feats);
|
||||
}
|
||||
}
|
||||
|
||||
@ -945,7 +945,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
|
||||
assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
|
||||
use ReprAttr::*;
|
||||
let mut acc = Vec::new();
|
||||
let diagnostic = &sess.parse_sess.span_diagnostic;
|
||||
let diagnostic = sess.dcx();
|
||||
|
||||
if let Some(items) = attr.meta_item_list() {
|
||||
for item in items {
|
||||
@ -985,7 +985,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
|
||||
Ok(literal) => acc.push(ReprPacked(literal)),
|
||||
Err(message) => literal_error = Some(message),
|
||||
};
|
||||
} else if matches!(name, sym::C | sym::simd | sym::transparent)
|
||||
} else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
|
||||
|| int_type_of_word(name).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
@ -1018,7 +1018,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
|
||||
});
|
||||
} else if matches!(
|
||||
meta_item.name_or_empty(),
|
||||
sym::C | sym::simd | sym::transparent
|
||||
sym::Rust | sym::C | sym::simd | sym::transparent
|
||||
) || int_type_of_word(meta_item.name_or_empty()).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
@ -1043,7 +1043,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
|
||||
);
|
||||
} else if matches!(
|
||||
meta_item.name_or_empty(),
|
||||
sym::C | sym::simd | sym::transparent
|
||||
sym::Rust | sym::C | sym::simd | sym::transparent
|
||||
) || int_type_of_word(meta_item.name_or_empty()).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
@ -1060,9 +1060,9 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
|
||||
// Not a word we recognize. This will be caught and reported by
|
||||
// the `check_mod_attrs` pass, but this pass doesn't always run
|
||||
// (e.g. if we only pretty-print the source), so we have to gate
|
||||
// the `delay_span_bug` call as follows:
|
||||
// the `span_delayed_bug` call as follows:
|
||||
if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) {
|
||||
diagnostic.delay_span_bug(item.span(), "unrecognized representation hint");
|
||||
diagnostic.span_delayed_bug(item.span(), "unrecognized representation hint");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
|
||||
//! to this crate.
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(let_chains)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
@ -14,9 +14,6 @@
|
||||
#[macro_use]
|
||||
extern crate rustc_macros;
|
||||
|
||||
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
|
||||
mod builtin;
|
||||
mod session_diagnostics;
|
||||
|
||||
@ -29,4 +26,4 @@ pub use rustc_ast::attr::*;
|
||||
|
||||
pub(crate) use rustc_session::HashStableContext;
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
@ -2,7 +2,7 @@ use std::num::IntErrorKind;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::{
|
||||
error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
|
||||
error_code, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
|
||||
};
|
||||
use rustc_macros::Diagnostic;
|
||||
use rustc_span::{Span, Symbol};
|
||||
@ -51,9 +51,9 @@ pub(crate) struct UnknownMetaItem<'a> {
|
||||
|
||||
// Manual implementation to be able to format `expected` items correctly.
|
||||
impl<'a> IntoDiagnostic<'a> for UnknownMetaItem<'_> {
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
||||
fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
||||
let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
|
||||
let mut diag = handler.struct_span_err_with_code(
|
||||
let mut diag = dcx.struct_span_err_with_code(
|
||||
self.span,
|
||||
fluent::attr_unknown_meta_item,
|
||||
error_code!(E0541),
|
||||
@ -201,8 +201,8 @@ pub(crate) struct UnsupportedLiteral {
|
||||
}
|
||||
|
||||
impl<'a> IntoDiagnostic<'a> for UnsupportedLiteral {
|
||||
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
||||
let mut diag = handler.struct_span_err_with_code(
|
||||
fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
||||
let mut diag = dcx.struct_span_err_with_code(
|
||||
self.span,
|
||||
match self.reason {
|
||||
UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic,
|
||||
|
||||
@ -20,9 +20,9 @@
|
||||
//! --cldr-tag latest --icuexport-tag latest -o src/data
|
||||
//! ```
|
||||
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![allow(elided_lifetimes_in_paths)]
|
||||
|
||||
mod data {
|
||||
|
||||
@ -6,7 +6,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
either = "1.5.0"
|
||||
itertools = "0.10.1"
|
||||
itertools = "0.11"
|
||||
polonius-engine = "0.13.0"
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
@ -19,7 +19,6 @@ rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_mir_dataflow = { path = "../rustc_mir_dataflow" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
|
||||
@ -107,7 +107,7 @@ impl LocalsStateAtExit {
|
||||
LocalsStateAtExit::AllAreInvalidated
|
||||
} else {
|
||||
let mut has_storage_dead = HasStorageDead(BitSet::new_empty(body.local_decls.len()));
|
||||
has_storage_dead.visit_body(&body);
|
||||
has_storage_dead.visit_body(body);
|
||||
let mut has_storage_dead_or_moved = has_storage_dead.0;
|
||||
for move_out in &move_data.moves {
|
||||
if let Some(index) = move_data.base_local(move_out.path) {
|
||||
@ -128,7 +128,7 @@ impl<'tcx> BorrowSet<'tcx> {
|
||||
) -> Self {
|
||||
let mut visitor = GatherBorrows {
|
||||
tcx,
|
||||
body: &body,
|
||||
body: body,
|
||||
location_map: Default::default(),
|
||||
activation_map: Default::default(),
|
||||
local_map: Default::default(),
|
||||
@ -140,7 +140,7 @@ impl<'tcx> BorrowSet<'tcx> {
|
||||
),
|
||||
};
|
||||
|
||||
for (block, block_data) in traversal::preorder(&body) {
|
||||
for (block, block_data) in traversal::preorder(body) {
|
||||
visitor.visit_basic_block_data(block, block_data);
|
||||
}
|
||||
|
||||
|
||||
@ -1,248 +0,0 @@
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::mir::visit::TyContext;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::{
|
||||
Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, SourceInfo, Statement,
|
||||
StatementKind, Terminator, TerminatorKind, UserTypeProjection,
|
||||
};
|
||||
use rustc_middle::ty::visit::TypeVisitable;
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
|
||||
|
||||
use crate::{
|
||||
borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, places_conflict,
|
||||
region_infer::values::LivenessValues,
|
||||
};
|
||||
|
||||
pub(super) fn generate_constraints<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
liveness_constraints: &mut LivenessValues<RegionVid>,
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
let mut cg = ConstraintGeneration {
|
||||
borrow_set,
|
||||
infcx,
|
||||
liveness_constraints,
|
||||
location_table,
|
||||
all_facts,
|
||||
body,
|
||||
};
|
||||
|
||||
for (bb, data) in body.basic_blocks.iter_enumerated() {
|
||||
cg.visit_basic_block_data(bb, data);
|
||||
}
|
||||
}
|
||||
|
||||
/// 'cg = the duration of the constraint generation process itself.
|
||||
struct ConstraintGeneration<'cg, 'tcx> {
|
||||
infcx: &'cg InferCtxt<'tcx>,
|
||||
all_facts: &'cg mut Option<AllFacts>,
|
||||
location_table: &'cg LocationTable,
|
||||
liveness_constraints: &'cg mut LivenessValues<RegionVid>,
|
||||
borrow_set: &'cg BorrowSet<'tcx>,
|
||||
body: &'cg Body<'tcx>,
|
||||
}
|
||||
|
||||
impl<'cg, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'tcx> {
|
||||
/// We sometimes have `args` within an rvalue, or within a
|
||||
/// call. Make them live at the location where they appear.
|
||||
fn visit_args(&mut self, args: &GenericArgsRef<'tcx>, location: Location) {
|
||||
self.add_regular_live_constraint(*args, location);
|
||||
self.super_args(args);
|
||||
}
|
||||
|
||||
/// We sometimes have `region` within an rvalue, or within a
|
||||
/// call. Make them live at the location where they appear.
|
||||
fn visit_region(&mut self, region: ty::Region<'tcx>, location: Location) {
|
||||
self.add_regular_live_constraint(region, location);
|
||||
self.super_region(region);
|
||||
}
|
||||
|
||||
/// We sometimes have `ty` within an rvalue, or within a
|
||||
/// call. Make them live at the location where they appear.
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>, ty_context: TyContext) {
|
||||
match ty_context {
|
||||
TyContext::ReturnTy(SourceInfo { span, .. })
|
||||
| TyContext::YieldTy(SourceInfo { span, .. })
|
||||
| TyContext::UserTy(span)
|
||||
| TyContext::LocalDecl { source_info: SourceInfo { span, .. }, .. } => {
|
||||
span_bug!(span, "should not be visiting outside of the CFG: {:?}", ty_context);
|
||||
}
|
||||
TyContext::Location(location) => {
|
||||
self.add_regular_live_constraint(ty, location);
|
||||
}
|
||||
}
|
||||
|
||||
self.super_ty(ty);
|
||||
}
|
||||
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
if let Some(all_facts) = self.all_facts {
|
||||
let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
all_facts.cfg_edge.push((
|
||||
self.location_table.start_index(location),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
|
||||
all_facts.cfg_edge.push((
|
||||
self.location_table.mid_index(location),
|
||||
self.location_table.start_index(location.successor_within_block()),
|
||||
));
|
||||
|
||||
// If there are borrows on this now dead local, we need to record them as `killed`.
|
||||
if let StatementKind::StorageDead(local) = statement.kind {
|
||||
record_killed_borrows_for_local(
|
||||
all_facts,
|
||||
self.borrow_set,
|
||||
self.location_table,
|
||||
local,
|
||||
location,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
||||
fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
// When we see `X = ...`, then kill borrows of
|
||||
// `(*X).foo` and so forth.
|
||||
self.record_killed_borrows_for_place(*place, location);
|
||||
|
||||
self.super_assign(place, rvalue, location);
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
if let Some(all_facts) = self.all_facts {
|
||||
let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
all_facts.cfg_edge.push((
|
||||
self.location_table.start_index(location),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
|
||||
let successor_blocks = terminator.successors();
|
||||
all_facts.cfg_edge.reserve(successor_blocks.size_hint().0);
|
||||
for successor_block in successor_blocks {
|
||||
all_facts.cfg_edge.push((
|
||||
self.location_table.mid_index(location),
|
||||
self.location_table.start_index(successor_block.start_location()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// A `Call` terminator's return value can be a local which has borrows,
|
||||
// so we need to record those as `killed` as well.
|
||||
if let TerminatorKind::Call { destination, .. } = terminator.kind {
|
||||
self.record_killed_borrows_for_place(destination, location);
|
||||
}
|
||||
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
|
||||
fn visit_ascribe_user_ty(
|
||||
&mut self,
|
||||
_place: &Place<'tcx>,
|
||||
_variance: ty::Variance,
|
||||
_user_ty: &UserTypeProjection,
|
||||
_location: Location,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> ConstraintGeneration<'cx, 'tcx> {
|
||||
/// Some variable with type `live_ty` is "regular live" at
|
||||
/// `location` -- i.e., it may be used later. This means that all
|
||||
/// regions appearing in the type `live_ty` must be live at
|
||||
/// `location`.
|
||||
fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
|
||||
where
|
||||
T: TypeVisitable<TyCtxt<'tcx>>,
|
||||
{
|
||||
debug!("add_regular_live_constraint(live_ty={:?}, location={:?})", live_ty, location);
|
||||
|
||||
self.infcx.tcx.for_each_free_region(&live_ty, |live_region| {
|
||||
let vid = live_region.as_var();
|
||||
self.liveness_constraints.add_element(vid, location);
|
||||
});
|
||||
}
|
||||
|
||||
/// When recording facts for Polonius, records the borrows on the specified place
|
||||
/// as `killed`. For example, when assigning to a local, or on a call's return destination.
|
||||
fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) {
|
||||
if let Some(all_facts) = self.all_facts {
|
||||
let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
|
||||
// Depending on the `Place` we're killing:
|
||||
// - if it's a local, or a single deref of a local,
|
||||
// we kill all the borrows on the local.
|
||||
// - if it's a deeper projection, we have to filter which
|
||||
// of the borrows are killed: the ones whose `borrowed_place`
|
||||
// conflicts with the `place`.
|
||||
match place.as_ref() {
|
||||
PlaceRef { local, projection: &[] }
|
||||
| PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
|
||||
debug!(
|
||||
"Recording `killed` facts for borrows of local={:?} at location={:?}",
|
||||
local, location
|
||||
);
|
||||
|
||||
record_killed_borrows_for_local(
|
||||
all_facts,
|
||||
self.borrow_set,
|
||||
self.location_table,
|
||||
local,
|
||||
location,
|
||||
);
|
||||
}
|
||||
|
||||
PlaceRef { local, projection: &[.., _] } => {
|
||||
// Kill conflicting borrows of the innermost local.
|
||||
debug!(
|
||||
"Recording `killed` facts for borrows of \
|
||||
innermost projected local={:?} at location={:?}",
|
||||
local, location
|
||||
);
|
||||
|
||||
if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
|
||||
for &borrow_index in borrow_indices {
|
||||
let places_conflict = places_conflict::places_conflict(
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
self.borrow_set[borrow_index].borrowed_place,
|
||||
place,
|
||||
places_conflict::PlaceConflictBias::NoOverlap,
|
||||
);
|
||||
|
||||
if places_conflict {
|
||||
let location_index = self.location_table.mid_index(location);
|
||||
all_facts.loan_killed_at.push((borrow_index, location_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When recording facts for Polonius, records the borrows on the specified local as `killed`.
|
||||
fn record_killed_borrows_for_local(
|
||||
all_facts: &mut AllFacts,
|
||||
borrow_set: &BorrowSet<'_>,
|
||||
location_table: &LocationTable,
|
||||
local: Local,
|
||||
location: Location,
|
||||
) {
|
||||
if let Some(borrow_indices) = borrow_set.local_map.get(&local) {
|
||||
all_facts.loan_killed_at.reserve(borrow_indices.len());
|
||||
for &borrow_index in borrow_indices {
|
||||
let location_index = location_table.mid_index(location);
|
||||
all_facts.loan_killed_at.push((borrow_index, location_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,6 +122,7 @@ rustc_index::newtype_index! {
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[orderable]
|
||||
#[debug_format = "ConstraintSccIndex({})"]
|
||||
pub struct ConstraintSccIndex {}
|
||||
}
|
||||
|
||||
@ -10,105 +10,93 @@ use rustc_middle::ty::RegionVid;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
|
||||
use rustc_mir_dataflow::ResultsVisitable;
|
||||
use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
|
||||
use rustc_mir_dataflow::{Analysis, Direction, Results};
|
||||
use rustc_mir_dataflow::{fmt::DebugWithContext, GenKill};
|
||||
use rustc_mir_dataflow::{Analysis, AnalysisDomain, Results};
|
||||
use std::fmt;
|
||||
|
||||
use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext};
|
||||
|
||||
/// A tuple with named fields that can hold either the results or the transient state of the
|
||||
/// dataflow analyses used by the borrow checker.
|
||||
#[derive(Debug)]
|
||||
pub struct BorrowckAnalyses<B, U, E> {
|
||||
pub borrows: B,
|
||||
pub uninits: U,
|
||||
pub ever_inits: E,
|
||||
}
|
||||
|
||||
/// The results of the dataflow analyses used by the borrow checker.
|
||||
pub type BorrowckResults<'mir, 'tcx> = BorrowckAnalyses<
|
||||
Results<'tcx, Borrows<'mir, 'tcx>>,
|
||||
Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>,
|
||||
Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>,
|
||||
>;
|
||||
pub struct BorrowckResults<'mir, 'tcx> {
|
||||
pub(crate) borrows: Results<'tcx, Borrows<'mir, 'tcx>>,
|
||||
pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>,
|
||||
pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>,
|
||||
}
|
||||
|
||||
/// The transient state of the dataflow analyses used by the borrow checker.
|
||||
pub type BorrowckFlowState<'mir, 'tcx> =
|
||||
<BorrowckResults<'mir, 'tcx> as ResultsVisitable<'tcx>>::FlowState;
|
||||
|
||||
macro_rules! impl_visitable {
|
||||
( $(
|
||||
$T:ident { $( $field:ident : $A:ident ),* $(,)? }
|
||||
)* ) => { $(
|
||||
impl<'tcx, $($A),*, D: Direction> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*>
|
||||
where
|
||||
$( $A: Analysis<'tcx, Direction = D>, )*
|
||||
{
|
||||
type Direction = D;
|
||||
type FlowState = $T<$( $A::Domain ),*>;
|
||||
|
||||
fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
|
||||
$T {
|
||||
$( $field: self.$field.analysis.bottom_value(body) ),*
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_to_block_entry(
|
||||
&self,
|
||||
state: &mut Self::FlowState,
|
||||
block: BasicBlock,
|
||||
) {
|
||||
$( state.$field.clone_from(&self.$field.entry_set_for_block(block)); )*
|
||||
}
|
||||
|
||||
fn reconstruct_before_statement_effect(
|
||||
&mut self,
|
||||
state: &mut Self::FlowState,
|
||||
stmt: &mir::Statement<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
$( self.$field.analysis
|
||||
.apply_before_statement_effect(&mut state.$field, stmt, loc); )*
|
||||
}
|
||||
|
||||
fn reconstruct_statement_effect(
|
||||
&mut self,
|
||||
state: &mut Self::FlowState,
|
||||
stmt: &mir::Statement<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
$( self.$field.analysis
|
||||
.apply_statement_effect(&mut state.$field, stmt, loc); )*
|
||||
}
|
||||
|
||||
fn reconstruct_before_terminator_effect(
|
||||
&mut self,
|
||||
state: &mut Self::FlowState,
|
||||
term: &mir::Terminator<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
$( self.$field.analysis
|
||||
.apply_before_terminator_effect(&mut state.$field, term, loc); )*
|
||||
}
|
||||
|
||||
fn reconstruct_terminator_effect(
|
||||
&mut self,
|
||||
state: &mut Self::FlowState,
|
||||
term: &mir::Terminator<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
$( self.$field.analysis
|
||||
.apply_terminator_effect(&mut state.$field, term, loc); )*
|
||||
}
|
||||
}
|
||||
)* }
|
||||
#[derive(Debug)]
|
||||
pub struct BorrowckFlowState<'mir, 'tcx> {
|
||||
pub(crate) borrows: <Borrows<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) uninits: <MaybeUninitializedPlaces<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
pub(crate) ever_inits: <EverInitializedPlaces<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
|
||||
}
|
||||
|
||||
impl_visitable! {
|
||||
BorrowckAnalyses { borrows: B, uninits: U, ever_inits: E }
|
||||
impl<'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'mir, 'tcx> {
|
||||
// All three analyses are forward, but we have to use just one here.
|
||||
type Direction = <Borrows<'mir, 'tcx> as AnalysisDomain<'tcx>>::Direction;
|
||||
type FlowState = BorrowckFlowState<'mir, 'tcx>;
|
||||
|
||||
fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
|
||||
BorrowckFlowState {
|
||||
borrows: self.borrows.analysis.bottom_value(body),
|
||||
uninits: self.uninits.analysis.bottom_value(body),
|
||||
ever_inits: self.ever_inits.analysis.bottom_value(body),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) {
|
||||
state.borrows.clone_from(&self.borrows.entry_set_for_block(block));
|
||||
state.uninits.clone_from(&self.uninits.entry_set_for_block(block));
|
||||
state.ever_inits.clone_from(&self.ever_inits.entry_set_for_block(block));
|
||||
}
|
||||
|
||||
fn reconstruct_before_statement_effect(
|
||||
&mut self,
|
||||
state: &mut Self::FlowState,
|
||||
stmt: &mir::Statement<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
self.borrows.analysis.apply_before_statement_effect(&mut state.borrows, stmt, loc);
|
||||
self.uninits.analysis.apply_before_statement_effect(&mut state.uninits, stmt, loc);
|
||||
self.ever_inits.analysis.apply_before_statement_effect(&mut state.ever_inits, stmt, loc);
|
||||
}
|
||||
|
||||
fn reconstruct_statement_effect(
|
||||
&mut self,
|
||||
state: &mut Self::FlowState,
|
||||
stmt: &mir::Statement<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
self.borrows.analysis.apply_statement_effect(&mut state.borrows, stmt, loc);
|
||||
self.uninits.analysis.apply_statement_effect(&mut state.uninits, stmt, loc);
|
||||
self.ever_inits.analysis.apply_statement_effect(&mut state.ever_inits, stmt, loc);
|
||||
}
|
||||
|
||||
fn reconstruct_before_terminator_effect(
|
||||
&mut self,
|
||||
state: &mut Self::FlowState,
|
||||
term: &mir::Terminator<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
self.borrows.analysis.apply_before_terminator_effect(&mut state.borrows, term, loc);
|
||||
self.uninits.analysis.apply_before_terminator_effect(&mut state.uninits, term, loc);
|
||||
self.ever_inits.analysis.apply_before_terminator_effect(&mut state.ever_inits, term, loc);
|
||||
}
|
||||
|
||||
fn reconstruct_terminator_effect(
|
||||
&mut self,
|
||||
state: &mut Self::FlowState,
|
||||
term: &mir::Terminator<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
self.borrows.analysis.apply_terminator_effect(&mut state.borrows, term, loc);
|
||||
self.uninits.analysis.apply_terminator_effect(&mut state.uninits, term, loc);
|
||||
self.ever_inits.analysis.apply_terminator_effect(&mut state.ever_inits, term, loc);
|
||||
}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[orderable]
|
||||
#[debug_format = "bw{}"]
|
||||
pub struct BorrowIndex {}
|
||||
}
|
||||
@ -120,24 +108,24 @@ 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, 'tcx> {
|
||||
pub struct Borrows<'mir, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'a Body<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
borrow_set: &'mir BorrowSet<'tcx>,
|
||||
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
|
||||
}
|
||||
|
||||
struct OutOfScopePrecomputer<'a, 'tcx> {
|
||||
struct OutOfScopePrecomputer<'mir, 'tcx> {
|
||||
visited: BitSet<mir::BasicBlock>,
|
||||
visit_stack: Vec<mir::BasicBlock>,
|
||||
body: &'a Body<'tcx>,
|
||||
regioncx: &'a RegionInferenceContext<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
regioncx: &'mir RegionInferenceContext<'tcx>,
|
||||
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> OutOfScopePrecomputer<'a, 'tcx> {
|
||||
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
|
||||
impl<'mir, 'tcx> OutOfScopePrecomputer<'mir, 'tcx> {
|
||||
fn new(body: &'mir Body<'tcx>, regioncx: &'mir RegionInferenceContext<'tcx>) -> Self {
|
||||
OutOfScopePrecomputer {
|
||||
visited: BitSet::new_empty(body.basic_blocks.len()),
|
||||
visit_stack: vec![],
|
||||
@ -240,17 +228,17 @@ pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
|
||||
prec.borrows_out_of_scope_at_location
|
||||
}
|
||||
|
||||
struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
|
||||
struct PoloniusOutOfScopePrecomputer<'mir, 'tcx> {
|
||||
visited: BitSet<mir::BasicBlock>,
|
||||
visit_stack: Vec<mir::BasicBlock>,
|
||||
body: &'a Body<'tcx>,
|
||||
regioncx: &'a RegionInferenceContext<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
regioncx: &'mir RegionInferenceContext<'tcx>,
|
||||
|
||||
loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
|
||||
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
|
||||
impl<'mir, 'tcx> PoloniusOutOfScopePrecomputer<'mir, 'tcx> {
|
||||
fn new(body: &'mir Body<'tcx>, regioncx: &'mir RegionInferenceContext<'tcx>) -> Self {
|
||||
Self {
|
||||
visited: BitSet::new_empty(body.basic_blocks.len()),
|
||||
visit_stack: vec![],
|
||||
@ -403,12 +391,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
||||
impl<'mir, 'tcx> Borrows<'mir, 'tcx> {
|
||||
pub fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'a Body<'tcx>,
|
||||
regioncx: &'a RegionInferenceContext<'tcx>,
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
body: &'mir Body<'tcx>,
|
||||
regioncx: &'mir RegionInferenceContext<'tcx>,
|
||||
borrow_set: &'mir BorrowSet<'tcx>,
|
||||
) -> Self {
|
||||
let mut borrows_out_of_scope_at_location =
|
||||
calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set);
|
||||
@ -431,7 +419,8 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
||||
|
||||
assert_eq!(
|
||||
borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location,
|
||||
"the loans out of scope must be the same as the borrows out of scope"
|
||||
"polonius loan scopes differ from NLL borrow scopes, for body {:?}",
|
||||
body.span,
|
||||
);
|
||||
|
||||
borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location;
|
||||
@ -596,7 +585,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
||||
|
||||
fn before_terminator_effect(
|
||||
&mut self,
|
||||
trans: &mut impl GenKill<Self::Idx>,
|
||||
trans: &mut Self::Domain,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
@ -623,7 +612,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
||||
|
||||
fn call_return_effect(
|
||||
&mut self,
|
||||
_trans: &mut impl GenKill<Self::Idx>,
|
||||
_trans: &mut Self::Domain,
|
||||
_block: mir::BasicBlock,
|
||||
_return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
|
||||
@ -10,6 +10,8 @@ use rustc_infer::infer::RegionVariableOrigin;
|
||||
use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _};
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::RePlaceholder;
|
||||
use rustc_middle::ty::Region;
|
||||
use rustc_middle::ty::RegionVid;
|
||||
use rustc_middle::ty::UniverseIndex;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
@ -205,6 +207,8 @@ trait TypeOpInfo<'tcx> {
|
||||
let span = cause.span;
|
||||
let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region);
|
||||
|
||||
debug!(?nice_error);
|
||||
|
||||
if let Some(nice_error) = nice_error {
|
||||
mbcx.buffer_error(nice_error);
|
||||
} else {
|
||||
@ -381,7 +385,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
|
||||
error_region: Option<ty::Region<'tcx>>,
|
||||
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
|
||||
// We generally shouldn't have errors here because the query was
|
||||
// already run, but there's no point using `delay_span_bug`
|
||||
// already run, but there's no point using `span_delayed_bug`
|
||||
// when we're going to emit an error here anyway.
|
||||
let _errors = ocx.select_all_or_error();
|
||||
let region_constraints = ocx.infcx.with_region_constraints(|r| r.clone());
|
||||
@ -404,19 +408,41 @@ fn try_extract_error_from_region_constraints<'tcx>(
|
||||
mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin,
|
||||
mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
|
||||
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
|
||||
let (sub_region, cause) =
|
||||
region_constraints.constraints.iter().find_map(|(constraint, cause)| {
|
||||
match *constraint {
|
||||
Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => {
|
||||
Some((sub, cause.clone()))
|
||||
}
|
||||
// FIXME: Should this check the universe of the var?
|
||||
Constraint::VarSubReg(vid, sup) if sup == placeholder_region => {
|
||||
Some((ty::Region::new_var(infcx.tcx, vid), cause.clone()))
|
||||
}
|
||||
_ => None,
|
||||
let matches =
|
||||
|a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) {
|
||||
(RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound,
|
||||
_ => a_region == b_region,
|
||||
};
|
||||
let check = |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| {
|
||||
match *constraint {
|
||||
Constraint::RegSubReg(sub, sup)
|
||||
if ((exact && sup == placeholder_region)
|
||||
|| (!exact && matches(sup, placeholder_region)))
|
||||
&& sup != sub =>
|
||||
{
|
||||
Some((sub, cause.clone()))
|
||||
}
|
||||
})?;
|
||||
// FIXME: Should this check the universe of the var?
|
||||
Constraint::VarSubReg(vid, sup)
|
||||
if ((exact && sup == placeholder_region)
|
||||
|| (!exact && matches(sup, placeholder_region))) =>
|
||||
{
|
||||
Some((ty::Region::new_var(infcx.tcx, vid), cause.clone()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
let mut info = region_constraints
|
||||
.constraints
|
||||
.iter()
|
||||
.find_map(|(constraint, cause)| check(constraint, cause, true));
|
||||
if info.is_none() {
|
||||
info = region_constraints
|
||||
.constraints
|
||||
.iter()
|
||||
.find_map(|(constraint, cause)| check(constraint, cause, false));
|
||||
}
|
||||
let (sub_region, cause) = info?;
|
||||
|
||||
debug!(?sub_region, "cause = {:#?}", cause);
|
||||
let error = match (error_region, *sub_region) {
|
||||
|
||||
@ -18,7 +18,7 @@ use rustc_middle::mir::{
|
||||
PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
|
||||
VarBindingForm,
|
||||
};
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty};
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty, TyCtxt};
|
||||
use rustc_middle::util::CallKind;
|
||||
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
@ -398,7 +398,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
let typeck = self.infcx.tcx.typeck(self.mir_def_id());
|
||||
let hir_id = hir.parent_id(expr.hir_id);
|
||||
if let Some(parent) = hir.find(hir_id) {
|
||||
if let Some(parent) = self.infcx.tcx.opt_hir_node(hir_id) {
|
||||
let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
|
||||
&& let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
|
||||
&& let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
|
||||
@ -413,7 +413,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
(None, &[][..], 0)
|
||||
};
|
||||
if let Some(def_id) = def_id
|
||||
&& let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id))
|
||||
&& let Some(node) = self
|
||||
.infcx
|
||||
.tcx
|
||||
.opt_hir_node(self.infcx.tcx.local_def_id_to_hir_id(def_id))
|
||||
&& let Some(fn_sig) = node.fn_sig()
|
||||
&& let Some(ident) = node.ident()
|
||||
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
|
||||
@ -445,7 +448,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
&& let hir::ExprKind::Path(hir::QPath::LangItem(
|
||||
LangItem::IntoIterIntoIter,
|
||||
_,
|
||||
_,
|
||||
)) = call_expr.kind
|
||||
{
|
||||
// Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
|
||||
@ -490,7 +492,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let mut spans = vec![];
|
||||
for init_idx in inits {
|
||||
let init = &self.move_data.inits[*init_idx];
|
||||
let span = init.span(&self.body);
|
||||
let span = init.span(self.body);
|
||||
if !span.is_dummy() {
|
||||
spans.push(span);
|
||||
}
|
||||
@ -518,7 +520,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let body = map.body(body_id);
|
||||
|
||||
let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
|
||||
visitor.visit_body(&body);
|
||||
visitor.visit_body(body);
|
||||
|
||||
let mut show_assign_sugg = false;
|
||||
let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
|
||||
@ -614,7 +616,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
|
||||
let mut visitor = LetVisitor { decl_span, sugg_span: None };
|
||||
visitor.visit_body(&body);
|
||||
visitor.visit_body(body);
|
||||
if let Some(span) = visitor.sugg_span {
|
||||
self.suggest_assign_value(&mut err, moved_place, span);
|
||||
}
|
||||
@ -779,7 +781,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
return;
|
||||
};
|
||||
// Try to find predicates on *generic params* that would allow copying `ty`
|
||||
let ocx = ObligationCtxt::new(&self.infcx);
|
||||
let ocx = ObligationCtxt::new(self.infcx);
|
||||
let copy_did = tcx.require_lang_item(LangItem::Copy, Some(span));
|
||||
let cause = ObligationCause::misc(span, self.mir_def_id());
|
||||
|
||||
@ -856,7 +858,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
self.explain_why_borrow_contains_point(location, borrow, None)
|
||||
.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
@ -903,7 +905,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
self.explain_why_borrow_contains_point(location, borrow, None)
|
||||
.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
@ -1136,7 +1138,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
});
|
||||
} else {
|
||||
issued_spans.var_subdiag(
|
||||
Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic),
|
||||
Some(self.infcx.tcx.sess.dcx()),
|
||||
&mut err,
|
||||
Some(issued_borrow.kind),
|
||||
|kind, var_span| {
|
||||
@ -1153,7 +1155,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
);
|
||||
|
||||
borrow_spans.var_subdiag(
|
||||
Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic),
|
||||
Some(self.infcx.tcx.sess.dcx()),
|
||||
&mut err,
|
||||
Some(gen_borrow_kind),
|
||||
|kind, var_span| {
|
||||
@ -1174,7 +1176,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
first_borrow_desc,
|
||||
@ -1318,7 +1320,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let tcx = self.infcx.tcx;
|
||||
let hir = tcx.hir();
|
||||
|
||||
let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return };
|
||||
let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
|
||||
let typeck_results = tcx.typeck(self.mir_def_id());
|
||||
|
||||
struct ExprFinder<'hir> {
|
||||
@ -1346,11 +1348,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// };
|
||||
// corresponding to the desugaring of a for loop `for <pat> in <head> { <body> }`.
|
||||
if let hir::ExprKind::Call(path, [arg]) = ex.kind
|
||||
&& let hir::ExprKind::Path(hir::QPath::LangItem(
|
||||
LangItem::IntoIterIntoIter,
|
||||
_,
|
||||
_,
|
||||
)) = path.kind
|
||||
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) =
|
||||
path.kind
|
||||
&& arg.span.contains(self.issue_span)
|
||||
{
|
||||
// Find `IntoIterator::into_iter(<head>)`
|
||||
@ -1368,10 +1367,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
..
|
||||
}) = stmt.kind
|
||||
&& let hir::ExprKind::Call(path, _args) = call.kind
|
||||
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _)) =
|
||||
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _)) =
|
||||
path.kind
|
||||
&& let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind
|
||||
&& let hir::QPath::LangItem(LangItem::OptionSome, pat_span, _) = path
|
||||
&& let hir::QPath::LangItem(LangItem::OptionSome, pat_span) = path
|
||||
&& call.span.contains(self.issue_span)
|
||||
{
|
||||
// Find `<pat>` and the span for the whole `for` loop.
|
||||
@ -1513,7 +1512,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let local_ty = self.body.local_decls[local].ty;
|
||||
|
||||
// Get the body the error happens in
|
||||
let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return };
|
||||
let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
|
||||
|
||||
let body_expr = hir.body(body_id).value;
|
||||
|
||||
@ -1562,7 +1561,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// Check that the parent of the closure is a method call,
|
||||
// with receiver matching with local's type (modulo refs)
|
||||
let parent = hir.parent_id(closure_expr.hir_id);
|
||||
if let hir::Node::Expr(parent) = hir.get(parent) {
|
||||
if let hir::Node::Expr(parent) = tcx.hir_node(parent) {
|
||||
if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind {
|
||||
let recv_ty = typeck_results.expr_ty(recv);
|
||||
|
||||
@ -1578,8 +1577,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
return;
|
||||
};
|
||||
let sig = args.as_closure().sig();
|
||||
let tupled_params =
|
||||
tcx.erase_late_bound_regions(sig.inputs().iter().next().unwrap().map_bound(|&b| b));
|
||||
let tupled_params = tcx.instantiate_bound_regions_with_erased(
|
||||
sig.inputs().iter().next().unwrap().map_bound(|&b| b),
|
||||
);
|
||||
let ty::Tuple(params) = tupled_params.kind() else { return };
|
||||
|
||||
// Find the first argument with a matching type, get its name
|
||||
@ -1638,15 +1638,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
issued_spans: &UseSpans<'tcx>,
|
||||
) {
|
||||
let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
|
||||
let hir = self.infcx.tcx.hir();
|
||||
|
||||
struct ExpressionFinder<'hir> {
|
||||
struct ExpressionFinder<'tcx> {
|
||||
capture_span: Span,
|
||||
closure_change_spans: Vec<Span>,
|
||||
closure_arg_span: Option<Span>,
|
||||
in_closure: bool,
|
||||
suggest_arg: String,
|
||||
hir: rustc_middle::hir::map::Map<'hir>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
closure_local_id: Option<hir::HirId>,
|
||||
closure_call_changes: Vec<(Span, String)>,
|
||||
}
|
||||
@ -1660,7 +1659,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
fn_decl: hir::FnDecl { inputs, .. },
|
||||
..
|
||||
}) = e.kind
|
||||
&& let Some(hir::Node::Expr(body)) = self.hir.find(body.hir_id)
|
||||
&& let Some(hir::Node::Expr(body)) = self.tcx.opt_hir_node(body.hir_id)
|
||||
{
|
||||
self.suggest_arg = "this: &Self".to_string();
|
||||
if inputs.len() > 0 {
|
||||
@ -1725,8 +1724,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
if let Some(hir::Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Fn(_fn_sig, body_id),
|
||||
..
|
||||
})) = hir.find(self.mir_hir_id())
|
||||
&& let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
|
||||
})) = self.infcx.tcx.opt_hir_node(self.mir_hir_id())
|
||||
&& let Some(hir::Node::Expr(expr)) = self.infcx.tcx.opt_hir_node(body_id.hir_id)
|
||||
{
|
||||
let mut finder = ExpressionFinder {
|
||||
capture_span: *capture_kind_span,
|
||||
@ -1736,7 +1735,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
suggest_arg: String::new(),
|
||||
closure_local_id: None,
|
||||
closure_call_changes: vec![],
|
||||
hir,
|
||||
tcx: self.infcx.tcx,
|
||||
};
|
||||
finder.visit_expr(expr);
|
||||
|
||||
@ -1931,7 +1930,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
|
||||
|
||||
let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
|
||||
let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
|
||||
let explanation = self.explain_why_borrow_contains_point(location, borrow, kind_place);
|
||||
|
||||
debug!(?place_desc, ?explanation);
|
||||
|
||||
@ -2000,14 +1999,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
(Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
|
||||
location,
|
||||
&name,
|
||||
&borrow,
|
||||
borrow,
|
||||
drop_span,
|
||||
borrow_spans,
|
||||
explanation,
|
||||
),
|
||||
(None, explanation) => self.report_temporary_value_does_not_live_long_enough(
|
||||
location,
|
||||
&borrow,
|
||||
borrow,
|
||||
drop_span,
|
||||
borrow_spans,
|
||||
proper_span,
|
||||
@ -2075,8 +2074,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
.map(|name| format!("function `{name}`"))
|
||||
.unwrap_or_else(|| {
|
||||
match &self.infcx.tcx.def_kind(self.mir_def_id()) {
|
||||
DefKind::Closure
|
||||
if self
|
||||
.infcx
|
||||
.tcx
|
||||
.is_coroutine(self.mir_def_id().to_def_id()) =>
|
||||
{
|
||||
"enclosing coroutine"
|
||||
}
|
||||
DefKind::Closure => "enclosing closure",
|
||||
DefKind::Coroutine => "enclosing coroutine",
|
||||
kind => bug!("expected closure or coroutine, found {:?}", kind),
|
||||
}
|
||||
.to_string()
|
||||
@ -2097,7 +2103,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
} else {
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
@ -2118,7 +2124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
@ -2179,7 +2185,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
@ -2290,7 +2296,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let proper_span = proper_span.source_callsite();
|
||||
if let Some(scope) = self.body.source_scopes.get(source_info.scope)
|
||||
&& let ClearCrossCrate::Set(scope_data) = &scope.local_data
|
||||
&& let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
|
||||
&& let Some(node) = self.infcx.tcx.opt_hir_node(scope_data.lint_root)
|
||||
&& let Some(id) = node.body_id()
|
||||
&& let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
|
||||
{
|
||||
@ -2348,7 +2354,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
err.note("the result of `format_args!` can only be assigned directly if no placeholders in it's arguments are used");
|
||||
err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used");
|
||||
err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
|
||||
}
|
||||
suggested = true;
|
||||
@ -2364,7 +2370,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
explanation.add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
@ -2513,12 +2519,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
CoroutineKind::Gen(kind) => match kind {
|
||||
CoroutineSource::Block => "gen block",
|
||||
CoroutineSource::Closure => "gen closure",
|
||||
_ => bug!("gen block/closure expected, but gen function found."),
|
||||
CoroutineSource::Fn => {
|
||||
bug!("gen block/closure expected, but gen function found.")
|
||||
}
|
||||
},
|
||||
CoroutineKind::AsyncGen(kind) => match kind {
|
||||
CoroutineSource::Block => "async gen block",
|
||||
CoroutineSource::Closure => "async gen closure",
|
||||
CoroutineSource::Fn => {
|
||||
bug!("gen block/closure expected, but gen function found.")
|
||||
}
|
||||
},
|
||||
CoroutineKind::Async(async_kind) => match async_kind {
|
||||
CoroutineSource::Block => "async block",
|
||||
CoroutineSource::Closure => "async closure",
|
||||
_ => bug!("async block/closure expected, but async function found."),
|
||||
CoroutineSource::Fn => {
|
||||
bug!("async block/closure expected, but async function found.")
|
||||
}
|
||||
},
|
||||
CoroutineKind::Coroutine => "coroutine",
|
||||
},
|
||||
@ -2841,7 +2858,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&mut err,
|
||||
"",
|
||||
@ -3019,7 +3036,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
let mut visitor = FakeReadCauseFinder { place, cause: None };
|
||||
visitor.visit_body(&self.body);
|
||||
visitor.visit_body(self.body);
|
||||
match visitor.cause {
|
||||
Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
|
||||
Some(FakeReadCause::ForIndex) => Some("indexing expression"),
|
||||
@ -3246,7 +3263,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
|
||||
debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
|
||||
let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
|
||||
let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
|
||||
let fn_hir_id = self.infcx.tcx.local_def_id_to_hir_id(did);
|
||||
let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
|
||||
|
||||
// We need to work out which arguments to highlight. We do this by looking
|
||||
|
||||
@ -5,12 +5,13 @@ use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_infer::infer::NllRegionVariableOrigin;
|
||||
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
|
||||
use rustc_middle::mir::{
|
||||
Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location,
|
||||
Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind,
|
||||
};
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::{self, RegionVid, TyCtxt};
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::{sym, DesugaringKind, Span};
|
||||
use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
|
||||
@ -86,7 +87,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind
|
||||
&& let [hir::PathSegment { ident, args: None, .. }] = p.segments
|
||||
&& let hir::def::Res::Local(hir_id) = p.res
|
||||
&& let Some(hir::Node::Pat(pat)) = tcx.hir().find(hir_id)
|
||||
&& let Some(hir::Node::Pat(pat)) = tcx.opt_hir_node(hir_id)
|
||||
{
|
||||
err.span_label(pat.span, format!("binding `{ident}` declared here"));
|
||||
}
|
||||
@ -290,12 +291,69 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
if let ConstraintCategory::Cast { unsize_to: Some(unsize_ty) } = category {
|
||||
self.add_object_lifetime_default_note(tcx, err, unsize_ty);
|
||||
}
|
||||
self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_object_lifetime_default_note(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
unsize_ty: Ty<'tcx>,
|
||||
) {
|
||||
if let ty::Adt(def, args) = unsize_ty.kind() {
|
||||
// We try to elaborate the object lifetime defaults and present those to the user. This should
|
||||
// make it clear where the region constraint is coming from.
|
||||
let generics = tcx.generics_of(def.did());
|
||||
|
||||
let mut has_dyn = false;
|
||||
let mut failed = false;
|
||||
|
||||
let elaborated_args = std::iter::zip(*args, &generics.params).map(|(arg, param)| {
|
||||
if let Some(ty::Dynamic(obj, _, ty::DynKind::Dyn)) = arg.as_type().map(Ty::kind) {
|
||||
let default = tcx.object_lifetime_default(param.def_id);
|
||||
|
||||
let re_static = tcx.lifetimes.re_static;
|
||||
|
||||
let implied_region = match default {
|
||||
// This is not entirely precise.
|
||||
ObjectLifetimeDefault::Empty => re_static,
|
||||
ObjectLifetimeDefault::Ambiguous => {
|
||||
failed = true;
|
||||
re_static
|
||||
}
|
||||
ObjectLifetimeDefault::Param(param_def_id) => {
|
||||
let index = generics.param_def_id_to_index[¶m_def_id] as usize;
|
||||
args.get(index).and_then(|arg| arg.as_region()).unwrap_or_else(|| {
|
||||
failed = true;
|
||||
re_static
|
||||
})
|
||||
}
|
||||
ObjectLifetimeDefault::Static => re_static,
|
||||
};
|
||||
|
||||
has_dyn = true;
|
||||
|
||||
Ty::new_dynamic(tcx, obj, implied_region, ty::DynKind::Dyn).into()
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
});
|
||||
let elaborated_ty = Ty::new_adt(tcx, *def, tcx.mk_args_from_iter(elaborated_args));
|
||||
|
||||
if has_dyn && !failed {
|
||||
err.note(format!(
|
||||
"due to object lifetime defaults, `{unsize_ty}` actually means `{elaborated_ty}`"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_lifetime_bound_suggestion_to_diagnostic(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
@ -364,7 +422,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
kind_place: Option<(WriteKind, Place<'tcx>)>,
|
||||
) -> BorrowExplanation<'tcx> {
|
||||
let regioncx = &self.regioncx;
|
||||
let body: &Body<'_> = &self.body;
|
||||
let body: &Body<'_> = self.body;
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
let borrow_region_vid = borrow.region;
|
||||
|
||||
@ -10,7 +10,8 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, Namespace};
|
||||
use rustc_hir::CoroutineKind;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_infer::infer::LateBoundRegionConversionTime;
|
||||
use rustc_infer::infer::BoundRegionConversionTime;
|
||||
use rustc_infer::traits::{FulfillmentErrorCode, SelectionError};
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::{
|
||||
AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location,
|
||||
@ -24,10 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{
|
||||
type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause,
|
||||
};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
|
||||
use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
|
||||
|
||||
use super::borrow_set::BorrowData;
|
||||
use super::MirBorrowckCtxt;
|
||||
@ -124,7 +124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let did = did.expect_local();
|
||||
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
|
||||
diag.eager_subdiagnostic(
|
||||
&self.infcx.tcx.sess.parse_sess.span_diagnostic,
|
||||
self.infcx.tcx.sess.dcx(),
|
||||
OnClosureNote::InvokedTwice {
|
||||
place_name: &ty::place_to_string_for_capture(
|
||||
self.infcx.tcx,
|
||||
@ -146,7 +146,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let did = did.expect_local();
|
||||
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
|
||||
diag.eager_subdiagnostic(
|
||||
&self.infcx.tcx.sess.parse_sess.span_diagnostic,
|
||||
self.infcx.tcx.sess.dcx(),
|
||||
OnClosureNote::MovedTwice {
|
||||
place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
|
||||
span: *span,
|
||||
@ -217,9 +217,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
projection: place.projection.split_at(index + 1).0,
|
||||
}) {
|
||||
let var_index = field.index();
|
||||
buf = self.upvars[var_index].place.to_string(self.infcx.tcx);
|
||||
buf = self.upvars[var_index].to_string(self.infcx.tcx);
|
||||
ok = Ok(());
|
||||
if !self.upvars[var_index].by_ref {
|
||||
if !self.upvars[var_index].is_by_ref() {
|
||||
buf.insert(0, '*');
|
||||
}
|
||||
} else {
|
||||
@ -250,7 +250,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
local,
|
||||
projection: place.projection.split_at(index + 1).0,
|
||||
}) {
|
||||
buf = self.upvars[field.index()].place.to_string(self.infcx.tcx);
|
||||
buf = self.upvars[field.index()].to_string(self.infcx.tcx);
|
||||
ok = Ok(());
|
||||
} else {
|
||||
let field_name = self.describe_field(
|
||||
@ -354,7 +354,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
ty::Adt(def, _) => {
|
||||
let variant = if let Some(idx) = variant_index {
|
||||
assert!(def.is_enum());
|
||||
&def.variant(idx)
|
||||
def.variant(idx)
|
||||
} else {
|
||||
def.non_enum_variant()
|
||||
};
|
||||
@ -462,7 +462,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// lifetimes without names with the value `'0`.
|
||||
if let ty::Ref(region, ..) = ty.kind() {
|
||||
match **region {
|
||||
ty::ReLateBound(_, ty::BoundRegion { kind: br, .. })
|
||||
ty::ReBound(_, ty::BoundRegion { kind: br, .. })
|
||||
| ty::RePlaceholder(ty::PlaceholderRegion {
|
||||
bound: ty::BoundRegion { kind: br, .. },
|
||||
..
|
||||
@ -482,7 +482,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
let region = if let ty::Ref(region, ..) = ty.kind() {
|
||||
match **region {
|
||||
ty::ReLateBound(_, ty::BoundRegion { kind: br, .. })
|
||||
ty::ReBound(_, ty::BoundRegion { kind: br, .. })
|
||||
| ty::RePlaceholder(ty::PlaceholderRegion {
|
||||
bound: ty::BoundRegion { kind: br, .. },
|
||||
..
|
||||
@ -624,7 +624,7 @@ impl UseSpans<'_> {
|
||||
/// Add a subdiagnostic to the use of the captured variable, if it exists.
|
||||
pub(super) fn var_subdiag(
|
||||
self,
|
||||
handler: Option<&rustc_errors::Handler>,
|
||||
dcx: Option<&rustc_errors::DiagCtxt>,
|
||||
err: &mut Diagnostic,
|
||||
kind: Option<rustc_middle::mir::BorrowKind>,
|
||||
f: impl FnOnce(Option<CoroutineKind>, Span) -> CaptureVarCause,
|
||||
@ -646,7 +646,7 @@ impl UseSpans<'_> {
|
||||
});
|
||||
};
|
||||
let diag = f(coroutine_kind, path_span);
|
||||
match handler {
|
||||
match dcx {
|
||||
Some(hd) => err.eager_subdiagnostic(hd, diag),
|
||||
None => err.subdiagnostic(diag),
|
||||
};
|
||||
@ -851,7 +851,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
{
|
||||
let Some((method_did, method_args)) = rustc_middle::util::find_self_call(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
target_temp,
|
||||
location.block,
|
||||
) else {
|
||||
@ -958,7 +958,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
"closure_span: def_id={:?} target_place={:?} places={:?}",
|
||||
def_id, target_place, places
|
||||
);
|
||||
let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
|
||||
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
|
||||
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
|
||||
if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr {
|
||||
@ -1043,12 +1043,43 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
|
||||
let self_arg = self_arg.unwrap();
|
||||
let mut has_sugg = false;
|
||||
let tcx = self.infcx.tcx;
|
||||
// Avoid pointing to the same function in multiple different
|
||||
// error messages.
|
||||
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
|
||||
self.explain_iterator_advancement_in_for_loop_if_applicable(
|
||||
err,
|
||||
span,
|
||||
&move_spans,
|
||||
);
|
||||
|
||||
let func = tcx.def_path_str(method_did);
|
||||
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
|
||||
func,
|
||||
place_name: place_name.clone(),
|
||||
span: self_arg.span,
|
||||
});
|
||||
}
|
||||
let parent_did = tcx.parent(method_did);
|
||||
let parent_self_ty =
|
||||
matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
|
||||
.then_some(parent_did)
|
||||
.and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
|
||||
ty::Adt(def, ..) => Some(def.did()),
|
||||
_ => None,
|
||||
});
|
||||
let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
|
||||
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
|
||||
});
|
||||
if is_option_or_result && maybe_reinitialized_locations_is_empty {
|
||||
err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
|
||||
}
|
||||
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
|
||||
let ty = moved_place.ty(self.body, tcx).ty;
|
||||
let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
|
||||
Some(def_id) => type_known_to_meet_bound_modulo_regions(
|
||||
&self.infcx,
|
||||
self.infcx,
|
||||
self.param_env,
|
||||
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
|
||||
def_id,
|
||||
@ -1108,72 +1139,92 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// Erase and shadow everything that could be passed to the new infcx.
|
||||
let ty = moved_place.ty(self.body, tcx).ty;
|
||||
|
||||
if let ty::Adt(def, args) = ty.kind()
|
||||
if let ty::Adt(def, args) = ty.peel_refs().kind()
|
||||
&& Some(def.did()) == tcx.lang_items().pin_type()
|
||||
&& let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
|
||||
&& let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
|
||||
fn_call_span,
|
||||
LateBoundRegionConversionTime::FnCall,
|
||||
BoundRegionConversionTime::FnCall,
|
||||
tcx.fn_sig(method_did).instantiate(tcx, method_args).input(0),
|
||||
)
|
||||
&& self.infcx.can_eq(self.param_env, ty, self_ty)
|
||||
{
|
||||
err.eager_subdiagnostic(
|
||||
&self.infcx.tcx.sess.parse_sess.span_diagnostic,
|
||||
self.infcx.tcx.sess.dcx(),
|
||||
CaptureReasonSuggest::FreshReborrow {
|
||||
span: move_span.shrink_to_hi(),
|
||||
},
|
||||
);
|
||||
has_sugg = true;
|
||||
}
|
||||
if let Some(clone_trait) = tcx.lang_items().clone_trait()
|
||||
&& let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
|
||||
&& let o = Obligation::new(
|
||||
tcx,
|
||||
ObligationCause::dummy(),
|
||||
self.param_env,
|
||||
ty::Binder::dummy(trait_ref),
|
||||
)
|
||||
&& self.infcx.predicate_must_hold_modulo_regions(&o)
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
move_span.shrink_to_hi(),
|
||||
"you can `clone` the value and consume it, but this might not be \
|
||||
your desired behavior",
|
||||
".clone()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
if let Some(clone_trait) = tcx.lang_items().clone_trait() {
|
||||
let sugg = if moved_place
|
||||
.iter_projections()
|
||||
.any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
|
||||
{
|
||||
vec![
|
||||
// We use the fully-qualified path because `.clone()` can
|
||||
// sometimes choose `<&T as Clone>` instead of `<T as Clone>`
|
||||
// when going through auto-deref, so this ensures that doesn't
|
||||
// happen, causing suggestions for `.clone().clone()`.
|
||||
(move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")),
|
||||
(move_span.shrink_to_hi(), ")".to_string()),
|
||||
]
|
||||
} else {
|
||||
vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
|
||||
};
|
||||
if let Some(errors) =
|
||||
self.infcx.could_impl_trait(clone_trait, ty, self.param_env)
|
||||
&& !has_sugg
|
||||
{
|
||||
let msg = match &errors[..] {
|
||||
[] => "you can `clone` the value and consume it, but this \
|
||||
might not be your desired behavior"
|
||||
.to_string(),
|
||||
[error] => {
|
||||
format!(
|
||||
"you could `clone` the value and consume it, if \
|
||||
the `{}` trait bound could be satisfied",
|
||||
error.obligation.predicate,
|
||||
)
|
||||
}
|
||||
[errors @ .., last] => {
|
||||
format!(
|
||||
"you could `clone` the value and consume it, if \
|
||||
the following trait bounds could be satisfied: {} \
|
||||
and `{}`",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| format!("`{}`", e.obligation.predicate))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
last.obligation.predicate,
|
||||
)
|
||||
}
|
||||
};
|
||||
err.multipart_suggestion_verbose(
|
||||
msg,
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
for error in errors {
|
||||
if let FulfillmentErrorCode::CodeSelectionError(
|
||||
SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
|
||||
pred,
|
||||
)) = error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
err,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Avoid pointing to the same function in multiple different
|
||||
// error messages.
|
||||
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
|
||||
self.explain_iterator_advancement_in_for_loop_if_applicable(
|
||||
err,
|
||||
span,
|
||||
&move_spans,
|
||||
);
|
||||
|
||||
let func = tcx.def_path_str(method_did);
|
||||
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
|
||||
func,
|
||||
place_name,
|
||||
span: self_arg.span,
|
||||
});
|
||||
}
|
||||
let parent_did = tcx.parent(method_did);
|
||||
let parent_self_ty =
|
||||
matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
|
||||
.then_some(parent_did)
|
||||
.and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
|
||||
ty::Adt(def, ..) => Some(def.did()),
|
||||
_ => None,
|
||||
});
|
||||
let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
|
||||
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
|
||||
});
|
||||
if is_option_or_result && maybe_reinitialized_locations_is_empty {
|
||||
err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
|
||||
}
|
||||
}
|
||||
// Other desugarings takes &self, which cannot cause a move
|
||||
_ => {}
|
||||
|
||||
@ -321,7 +321,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
|
||||
let deref_base = match deref_target_place.projection.as_ref() {
|
||||
[proj_base @ .., ProjectionElem::Deref] => {
|
||||
PlaceRef { local: deref_target_place.local, projection: &proj_base }
|
||||
PlaceRef { local: deref_target_place.local, projection: proj_base }
|
||||
}
|
||||
_ => bug!("deref_target_place is not a deref projection"),
|
||||
};
|
||||
@ -363,8 +363,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
format!("captured variable in an `{closure_kind}` closure");
|
||||
|
||||
let upvar = &self.upvars[upvar_field.unwrap().index()];
|
||||
let upvar_hir_id = upvar.place.get_root_variable();
|
||||
let upvar_name = upvar.place.to_string(self.infcx.tcx);
|
||||
let upvar_hir_id = upvar.get_root_variable();
|
||||
let upvar_name = upvar.to_string(self.infcx.tcx);
|
||||
let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id);
|
||||
|
||||
let place_name = self.describe_any_place(move_place.as_ref());
|
||||
@ -583,7 +583,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
is_partial_move: false,
|
||||
ty: bind_to.ty,
|
||||
place: &place_desc,
|
||||
place: place_desc,
|
||||
span: binding_span,
|
||||
});
|
||||
}
|
||||
@ -607,7 +607,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
|
||||
if let Some(adt) = local_ty.ty_adt_def()
|
||||
&& adt.repr().packed()
|
||||
&& let ExpnKind::Macro(MacroKind::Derive, name) = self.body.span.ctxt().outer_expn_data().kind
|
||||
&& let ExpnKind::Macro(MacroKind::Derive, name) =
|
||||
self.body.span.ctxt().outer_expn_data().kind
|
||||
{
|
||||
err.note(format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour"));
|
||||
}
|
||||
|
||||
@ -3,8 +3,9 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::Node;
|
||||
use rustc_infer::traits;
|
||||
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
|
||||
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt};
|
||||
use rustc_middle::{
|
||||
hir::place::PlaceBase,
|
||||
mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
|
||||
@ -12,6 +13,8 @@ use rustc_middle::{
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::{sym, BytePos, DesugaringKind, Span};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
|
||||
|
||||
use crate::diagnostics::BorrowedContentSource;
|
||||
use crate::util::FindAssignments;
|
||||
@ -66,7 +69,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
));
|
||||
|
||||
let imm_borrow_derefed = self.upvars[upvar_index.index()]
|
||||
.place
|
||||
.place
|
||||
.deref_tys()
|
||||
.any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
|
||||
@ -85,7 +87,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
|
||||
reason = ", as it is not declared as mutable".to_string();
|
||||
} else {
|
||||
let name = self.upvars[upvar_index.index()].place.to_string(self.infcx.tcx);
|
||||
let name = self.upvars[upvar_index.index()].to_string(self.infcx.tcx);
|
||||
reason = format!(", as `{name}` is not declared as mutable");
|
||||
}
|
||||
}
|
||||
@ -388,13 +390,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
|
||||
));
|
||||
|
||||
let captured_place = &self.upvars[upvar_index.index()].place;
|
||||
let captured_place = self.upvars[upvar_index.index()];
|
||||
|
||||
err.span_label(span, format!("cannot {act}"));
|
||||
|
||||
let upvar_hir_id = captured_place.get_root_variable();
|
||||
|
||||
if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
|
||||
if let Some(Node::Pat(pat)) = self.infcx.tcx.opt_hir_node(upvar_hir_id)
|
||||
&& let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, upvar_ident, _) =
|
||||
pat.kind
|
||||
{
|
||||
@ -659,9 +661,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
if self.body.local_kind(local) != LocalKind::Arg {
|
||||
return (false, None);
|
||||
}
|
||||
let hir_map = self.infcx.tcx.hir();
|
||||
let my_def = self.body.source.def_id();
|
||||
let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
|
||||
let my_hir = self.infcx.tcx.local_def_id_to_hir_id(my_def.as_local().unwrap());
|
||||
let Some(td) =
|
||||
self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
|
||||
else {
|
||||
@ -669,7 +670,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
};
|
||||
(
|
||||
true,
|
||||
td.as_local().and_then(|tld| match hir_map.find_by_def_id(tld) {
|
||||
td.as_local().and_then(|tld| match self.infcx.tcx.opt_hir_node_by_def_id(tld) {
|
||||
Some(Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Trait(_, _, _, _, items),
|
||||
..
|
||||
@ -680,25 +681,27 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
if !matches!(k, hir::AssocItemKind::Fn { .. }) {
|
||||
continue;
|
||||
}
|
||||
if hir_map.name(hi) != hir_map.name(my_hir) {
|
||||
if self.infcx.tcx.hir().name(hi) != self.infcx.tcx.hir().name(my_hir) {
|
||||
continue;
|
||||
}
|
||||
f_in_trait_opt = Some(hi);
|
||||
break;
|
||||
}
|
||||
f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
|
||||
Some(Node::TraitItem(hir::TraitItem {
|
||||
kind:
|
||||
hir::TraitItemKind::Fn(
|
||||
hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
|
||||
_,
|
||||
),
|
||||
..
|
||||
})) => {
|
||||
let hir::Ty { span, .. } = inputs[local.index() - 1];
|
||||
Some(span)
|
||||
f_in_trait_opt.and_then(|f_in_trait| {
|
||||
match self.infcx.tcx.opt_hir_node(f_in_trait) {
|
||||
Some(Node::TraitItem(hir::TraitItem {
|
||||
kind:
|
||||
hir::TraitItemKind::Fn(
|
||||
hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
|
||||
_,
|
||||
),
|
||||
..
|
||||
})) => {
|
||||
let hir::Ty { span, .. } = inputs[local.index() - 1];
|
||||
Some(span)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
@ -739,12 +742,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
let hir_map = self.infcx.tcx.hir();
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
|
||||
&& let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
let body = hir_map.body(body_id);
|
||||
let body = self.infcx.tcx.hir().body(body_id);
|
||||
let mut v = BindingFinder { span: pat_span, hir_id: None };
|
||||
v.visit_body(body);
|
||||
v.hir_id
|
||||
@ -760,7 +762,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
&& let Some(hir::Node::Local(hir::Local {
|
||||
pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
|
||||
..
|
||||
})) = hir_map.find(hir_id)
|
||||
})) = self.infcx.tcx.opt_hir_node(hir_id)
|
||||
&& let Ok(name) =
|
||||
self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
|
||||
{
|
||||
@ -940,7 +942,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
let closure_id = self.mir_hir_id();
|
||||
let closure_span = self.infcx.tcx.def_span(self.mir_def_id());
|
||||
let fn_call_id = hir.parent_id(closure_id);
|
||||
let node = hir.get(fn_call_id);
|
||||
let node = self.infcx.tcx.hir_node(fn_call_id);
|
||||
let def_id = hir.enclosing_body_owner(fn_call_id);
|
||||
let mut look_at_return = true;
|
||||
// If we can detect the expression to be an `fn` call where the closure was an argument,
|
||||
@ -999,7 +1001,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
if look_at_return && hir.get_return_block(closure_id).is_some() {
|
||||
// ...otherwise we are probably in the tail expression of the function, point at the
|
||||
// return type.
|
||||
match hir.get_by_def_id(hir.get_parent_item(fn_call_id).def_id) {
|
||||
match self.infcx.tcx.hir_node_by_def_id(hir.get_parent_item(fn_call_id).def_id) {
|
||||
hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
|
||||
| hir::Node::TraitItem(hir::TraitItem {
|
||||
ident,
|
||||
@ -1197,12 +1199,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
hir::intravisit::walk_stmt(self, s);
|
||||
}
|
||||
}
|
||||
let hir_map = self.infcx.tcx.hir();
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = if let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id)
|
||||
&& let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
|
||||
{
|
||||
let body = hir_map.body(body_id);
|
||||
let body = self.infcx.tcx.hir().body(body_id);
|
||||
let mut v = BindingFinder { span: err_label_span, hir_id: None };
|
||||
v.visit_body(body);
|
||||
v.hir_id
|
||||
@ -1211,8 +1212,105 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
};
|
||||
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
|
||||
&& let Some(hir::Node::Local(local)) = self.infcx.tcx.opt_hir_node(hir_id)
|
||||
{
|
||||
let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
|
||||
if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
|
||||
&& let Some(expr) = local.init
|
||||
&& let ty = tables.node_type_opt(expr.hir_id)
|
||||
&& let Some(ty) = ty
|
||||
&& let ty::Ref(..) = ty.kind()
|
||||
{
|
||||
match self
|
||||
.infcx
|
||||
.could_impl_trait(clone_trait, ty.peel_refs(), self.param_env)
|
||||
.as_deref()
|
||||
{
|
||||
Some([]) => {
|
||||
// The type implements Clone.
|
||||
err.span_help(
|
||||
expr.span,
|
||||
format!(
|
||||
"you can `clone` the `{}` value and consume it, but this \
|
||||
might not be your desired behavior",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
|
||||
expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone`, so this call clones \
|
||||
the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
}
|
||||
// The type doesn't implement Clone.
|
||||
let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
|
||||
self.infcx.tcx,
|
||||
clone_trait,
|
||||
[ty.peel_refs()],
|
||||
));
|
||||
let obligation = traits::Obligation::new(
|
||||
self.infcx.tcx,
|
||||
traits::ObligationCause::dummy(),
|
||||
self.param_env,
|
||||
trait_ref,
|
||||
);
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&obligation,
|
||||
err,
|
||||
trait_ref.to_predicate(self.infcx.tcx),
|
||||
);
|
||||
}
|
||||
Some(errors) => {
|
||||
if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
|
||||
expr.kind
|
||||
&& segment.ident.name == sym::clone
|
||||
{
|
||||
err.span_help(
|
||||
span,
|
||||
format!(
|
||||
"`{}` doesn't implement `Clone` because its \
|
||||
implementations trait bounds could not be met, so \
|
||||
this call clones the reference `{ty}`",
|
||||
ty.peel_refs(),
|
||||
),
|
||||
);
|
||||
err.note(format!(
|
||||
"the following trait bounds weren't met: {}",
|
||||
errors
|
||||
.iter()
|
||||
.map(|e| e.obligation.predicate.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
));
|
||||
}
|
||||
// The type doesn't implement Clone because of unmet obligations.
|
||||
for error in errors {
|
||||
if let traits::FulfillmentErrorCode::CodeSelectionError(
|
||||
traits::SelectionError::Unimplemented,
|
||||
) = error.code
|
||||
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
|
||||
pred,
|
||||
)) = error.obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
self.infcx.err_ctxt().suggest_derive(
|
||||
&error.obligation,
|
||||
err,
|
||||
error.obligation.predicate.kind().rebind(pred),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, message),
|
||||
None => {
|
||||
@ -1397,7 +1495,7 @@ fn get_mut_span_in_struct_field<'tcx>(
|
||||
&& let ty::Adt(def, _) = ty.kind()
|
||||
&& let field = def.all_fields().nth(field.index())?
|
||||
// Use the HIR types to construct the diagnostic message.
|
||||
&& let node = tcx.hir().find_by_def_id(field.did.as_local()?)?
|
||||
&& let node = tcx.opt_hir_node_by_def_id(field.did.as_local()?)?
|
||||
// Now we're dealing with the actual struct that we're going to suggest a change to,
|
||||
// we can expect a field that is an immutable reference to a type.
|
||||
&& let hir::Node::Field(field) = node
|
||||
|
||||
@ -50,8 +50,8 @@ impl OutlivesSuggestionBuilder {
|
||||
// naming the `'self` lifetime in methods, etc.
|
||||
fn region_name_is_suggestable(name: &RegionName) -> bool {
|
||||
match name.source {
|
||||
RegionNameSource::NamedEarlyBoundRegion(..)
|
||||
| RegionNameSource::NamedFreeRegion(..)
|
||||
RegionNameSource::NamedEarlyParamRegion(..)
|
||||
| RegionNameSource::NamedLateParamRegion(..)
|
||||
| RegionNameSource::Static => true,
|
||||
|
||||
// Don't give suggestions for upvars, closure return types, or other unnameable
|
||||
@ -206,7 +206,7 @@ impl OutlivesSuggestionBuilder {
|
||||
// If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
|
||||
// list of diagnostics.
|
||||
let mut diag = if suggested.len() == 1 {
|
||||
mbcx.infcx.tcx.sess.diagnostic().struct_help(match suggested.last().unwrap() {
|
||||
mbcx.infcx.tcx.sess.dcx().struct_help(match suggested.last().unwrap() {
|
||||
SuggestedConstraint::Outlives(a, bs) => {
|
||||
let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
|
||||
format!("add bound `{a}: {}`", bs.join(" + "))
|
||||
@ -223,7 +223,7 @@ impl OutlivesSuggestionBuilder {
|
||||
.infcx
|
||||
.tcx
|
||||
.sess
|
||||
.diagnostic()
|
||||
.dcx()
|
||||
.struct_help("the following changes may resolve your lifetime errors");
|
||||
|
||||
// Add suggestions.
|
||||
|
||||
@ -35,7 +35,7 @@ use crate::session_diagnostics::{
|
||||
LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote,
|
||||
};
|
||||
|
||||
use super::{OutlivesSuggestionBuilder, RegionName};
|
||||
use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource};
|
||||
use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
|
||||
use crate::{
|
||||
nll::ConstraintDescription,
|
||||
@ -53,7 +53,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
|
||||
ConstraintCategory::Yield => "yielding this value ",
|
||||
ConstraintCategory::UseAsConst => "using this value as a constant ",
|
||||
ConstraintCategory::UseAsStatic => "using this value as a static ",
|
||||
ConstraintCategory::Cast => "cast ",
|
||||
ConstraintCategory::Cast { .. } => "cast ",
|
||||
ConstraintCategory::CallArgument(_) => "argument ",
|
||||
ConstraintCategory::TypeAnnotation => "type annotation ",
|
||||
ConstraintCategory::ClosureBounds => "closure body ",
|
||||
@ -84,7 +84,7 @@ impl<'tcx> RegionErrors<'tcx> {
|
||||
#[track_caller]
|
||||
pub fn push(&mut self, val: impl Into<RegionErrorKind<'tcx>>) {
|
||||
let val = val.into();
|
||||
self.1.sess.delay_span_bug(DUMMY_SP, format!("{val:?}"));
|
||||
self.1.sess.span_delayed_bug(DUMMY_SP, format!("{val:?}"));
|
||||
self.0.push(val);
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
@ -95,6 +95,12 @@ impl<'tcx> RegionErrors<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RegionErrors<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("RegionErrors").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum RegionErrorKind<'tcx> {
|
||||
/// A generic bound failure for a type test (`T: 'a`).
|
||||
@ -181,8 +187,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
|
||||
/// Returns `true` if a closure is inferred to be an `FnMut` closure.
|
||||
fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
|
||||
if let Some(ty::ReFree(free_region)) = self.to_error_region(fr).as_deref()
|
||||
&& let ty::BoundRegionKind::BrEnv = free_region.bound_region
|
||||
if let Some(ty::ReLateParam(late_param)) = self.to_error_region(fr).as_deref()
|
||||
&& let ty::BoundRegionKind::BrEnv = late_param.bound_region
|
||||
&& let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty
|
||||
{
|
||||
return args.as_closure().kind() == ty::ClosureKind::FnMut;
|
||||
@ -209,7 +215,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
.map(|placeholder| {
|
||||
if let Some(id) = placeholder.bound.kind.get_id()
|
||||
&& let Some(placeholder_id) = id.as_local()
|
||||
&& let gat_hir_id = hir.local_def_id_to_hir_id(placeholder_id)
|
||||
&& let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id)
|
||||
&& let Some(generics_impl) = hir.get_parent(gat_hir_id).generics()
|
||||
{
|
||||
Some((gat_hir_id, generics_impl))
|
||||
@ -230,7 +236,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
};
|
||||
if bound_generic_params
|
||||
.iter()
|
||||
.rfind(|bgp| hir.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
|
||||
.rfind(|bgp| self.infcx.tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
|
||||
.is_some()
|
||||
{
|
||||
for bound in *bounds {
|
||||
@ -599,7 +605,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
};
|
||||
|
||||
let captured_place = &self.upvars[upvar_field.index()].place;
|
||||
let defined_hir = match captured_place.place.base {
|
||||
let defined_hir = match captured_place.base {
|
||||
PlaceBase::Local(hirid) => Some(hirid),
|
||||
PlaceBase::Upvar(upvar) => Some(upvar.var_path.hir_id),
|
||||
_ => None,
|
||||
@ -644,14 +650,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
|
||||
let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&self.upvars,
|
||||
errci.fr,
|
||||
);
|
||||
let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
&self.upvars,
|
||||
errci.outlived_fr,
|
||||
@ -757,7 +763,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
let err = LifetimeOutliveErr { span: *span };
|
||||
let mut diag = self.infcx.tcx.sess.create_err(err);
|
||||
|
||||
let fr_name = self.give_region_a_name(*fr).unwrap();
|
||||
// In certain scenarios, such as the one described in issue #118021,
|
||||
// we might encounter a lifetime that cannot be named.
|
||||
// These situations are bound to result in errors.
|
||||
// To prevent an immediate ICE, we opt to create a dummy name instead.
|
||||
let fr_name = self.give_region_a_name(*fr).unwrap_or(RegionName {
|
||||
name: kw::UnderscoreLifetime,
|
||||
source: RegionNameSource::Static,
|
||||
});
|
||||
fr_name.highlight_region_name(&mut diag);
|
||||
let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
|
||||
outlived_fr_name.highlight_region_name(&mut diag);
|
||||
@ -958,7 +971,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
for found_did in found_dids {
|
||||
let mut traits = vec![];
|
||||
let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
|
||||
hir_v.visit_ty(&self_ty);
|
||||
hir_v.visit_ty(self_ty);
|
||||
debug!("trait spans found: {:?}", traits);
|
||||
for span in &traits {
|
||||
let mut multi_span: MultiSpan = vec![*span].into();
|
||||
@ -995,7 +1008,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
.infcx
|
||||
.tcx
|
||||
.is_suitable_region(sub)
|
||||
.and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion))
|
||||
.and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.bound_region))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@ -1004,7 +1017,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
.infcx
|
||||
.tcx
|
||||
.is_suitable_region(sup)
|
||||
.and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion))
|
||||
.and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.bound_region))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -23,14 +23,14 @@ pub(crate) struct RegionName {
|
||||
}
|
||||
|
||||
/// Denotes the source of a region that is named by a `RegionName`. For example, a free region that
|
||||
/// was named by the user would get `NamedFreeRegion` and `'static` lifetime would get `Static`.
|
||||
/// was named by the user would get `NamedLateParamRegion` and `'static` lifetime would get `Static`.
|
||||
/// This helps to print the right kinds of diagnostics.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum RegionNameSource {
|
||||
/// A bound (not free) region that was instantiated at the def site (not an HRTB).
|
||||
NamedEarlyBoundRegion(Span),
|
||||
NamedEarlyParamRegion(Span),
|
||||
/// A free region that the user has a name (`'a`) for.
|
||||
NamedFreeRegion(Span),
|
||||
NamedLateParamRegion(Span),
|
||||
/// The `'static` region.
|
||||
Static,
|
||||
/// The free region corresponding to the environment of a closure.
|
||||
@ -69,8 +69,8 @@ pub(crate) enum RegionNameHighlight {
|
||||
impl RegionName {
|
||||
pub(crate) fn was_named(&self) -> bool {
|
||||
match self.source {
|
||||
RegionNameSource::NamedEarlyBoundRegion(..)
|
||||
| RegionNameSource::NamedFreeRegion(..)
|
||||
RegionNameSource::NamedEarlyParamRegion(..)
|
||||
| RegionNameSource::NamedLateParamRegion(..)
|
||||
| RegionNameSource::Static => true,
|
||||
RegionNameSource::SynthesizedFreeEnvRegion(..)
|
||||
| RegionNameSource::AnonRegionFromArgument(..)
|
||||
@ -85,8 +85,8 @@ impl RegionName {
|
||||
pub(crate) fn span(&self) -> Option<Span> {
|
||||
match self.source {
|
||||
RegionNameSource::Static => None,
|
||||
RegionNameSource::NamedEarlyBoundRegion(span)
|
||||
| RegionNameSource::NamedFreeRegion(span)
|
||||
RegionNameSource::NamedEarlyParamRegion(span)
|
||||
| RegionNameSource::NamedLateParamRegion(span)
|
||||
| RegionNameSource::SynthesizedFreeEnvRegion(span, _)
|
||||
| RegionNameSource::AnonRegionFromUpvar(span, _)
|
||||
| RegionNameSource::AnonRegionFromYieldTy(span, _)
|
||||
@ -104,8 +104,8 @@ impl RegionName {
|
||||
|
||||
pub(crate) fn highlight_region_name(&self, diag: &mut Diagnostic) {
|
||||
match &self.source {
|
||||
RegionNameSource::NamedFreeRegion(span)
|
||||
| RegionNameSource::NamedEarlyBoundRegion(span) => {
|
||||
RegionNameSource::NamedLateParamRegion(span)
|
||||
| RegionNameSource::NamedEarlyParamRegion(span) => {
|
||||
diag.span_label(*span, format!("lifetime `{self}` defined here"));
|
||||
}
|
||||
RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
|
||||
@ -199,7 +199,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
}
|
||||
|
||||
pub(crate) fn mir_hir_id(&self) -> hir::HirId {
|
||||
self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id())
|
||||
self.infcx.tcx.local_def_id_to_hir_id(self.mir_def_id())
|
||||
}
|
||||
|
||||
/// Generate a synthetic region named `'N`, where `N` is the next value of the counter. Then,
|
||||
@ -280,28 +280,31 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
|
||||
debug!("give_region_a_name: error_region = {:?}", error_region);
|
||||
match *error_region {
|
||||
ty::ReEarlyBound(ebr) => ebr.has_name().then(|| {
|
||||
ty::ReEarlyParam(ebr) => ebr.has_name().then(|| {
|
||||
let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP);
|
||||
RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyBoundRegion(span) }
|
||||
RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) }
|
||||
}),
|
||||
|
||||
ty::ReStatic => {
|
||||
Some(RegionName { name: kw::StaticLifetime, source: RegionNameSource::Static })
|
||||
}
|
||||
|
||||
ty::ReFree(free_region) => match free_region.bound_region {
|
||||
ty::ReLateParam(late_param) => match late_param.bound_region {
|
||||
ty::BoundRegionKind::BrNamed(region_def_id, name) => {
|
||||
// Get the span to point to, even if we don't use the name.
|
||||
let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP);
|
||||
debug!(
|
||||
"bound region named: {:?}, is_named: {:?}",
|
||||
name,
|
||||
free_region.bound_region.is_named()
|
||||
late_param.bound_region.is_named()
|
||||
);
|
||||
|
||||
if free_region.bound_region.is_named() {
|
||||
if late_param.bound_region.is_named() {
|
||||
// A named region that is actually named.
|
||||
Some(RegionName { name, source: RegionNameSource::NamedFreeRegion(span) })
|
||||
Some(RegionName {
|
||||
name,
|
||||
source: RegionNameSource::NamedLateParamRegion(span),
|
||||
})
|
||||
} else if tcx.asyncness(self.mir_hir_id().owner).is_async() {
|
||||
// If we spuriously thought that the region is named, we should let the
|
||||
// system generate a true name for error messages. Currently this can
|
||||
@ -357,7 +360,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
ty::BoundRegionKind::BrAnon => None,
|
||||
},
|
||||
|
||||
ty::ReLateBound(..)
|
||||
ty::ReBound(..)
|
||||
| ty::ReVar(..)
|
||||
| ty::RePlaceholder(..)
|
||||
| ty::ReErased
|
||||
@ -384,7 +387,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
let arg_ty = self.regioncx.universal_regions().unnormalized_input_tys
|
||||
[implicit_inputs + argument_index];
|
||||
let (_, span) = self.regioncx.get_argument_name_and_span_for_region(
|
||||
&self.body,
|
||||
self.body,
|
||||
&self.local_names,
|
||||
argument_index,
|
||||
);
|
||||
@ -616,8 +619,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
_,
|
||||
) => {
|
||||
// HIR lowering sometimes doesn't catch this in erroneous
|
||||
// programs, so we need to use delay_span_bug here. See #82126.
|
||||
self.infcx.tcx.sess.delay_span_bug(
|
||||
// programs, so we need to use span_delayed_bug here. See #82126.
|
||||
self.infcx.tcx.sess.span_delayed_bug(
|
||||
hir_arg.span(),
|
||||
format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"),
|
||||
);
|
||||
@ -669,7 +672,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
|
||||
let mir_hir_id = self.mir_hir_id();
|
||||
|
||||
let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) {
|
||||
let (return_span, mir_description, hir_ty) = match tcx.hir_node(mir_hir_id) {
|
||||
hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, body, fn_decl_span, .. }),
|
||||
..
|
||||
@ -681,12 +684,12 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)),
|
||||
};
|
||||
let mir_description = match hir.body(body).coroutine_kind {
|
||||
Some(hir::CoroutineKind::Async(gen)) => match gen {
|
||||
Some(hir::CoroutineKind::Async(src)) => match src {
|
||||
hir::CoroutineSource::Block => " of async block",
|
||||
hir::CoroutineSource::Closure => " of async closure",
|
||||
hir::CoroutineSource::Fn => {
|
||||
let parent_item =
|
||||
hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
let output = &parent_item
|
||||
.fn_decl()
|
||||
.expect("coroutine lowered from async fn should be in fn")
|
||||
@ -698,12 +701,12 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
" of async function"
|
||||
}
|
||||
},
|
||||
Some(hir::CoroutineKind::Gen(gen)) => match gen {
|
||||
Some(hir::CoroutineKind::Gen(src)) => match src {
|
||||
hir::CoroutineSource::Block => " of gen block",
|
||||
hir::CoroutineSource::Closure => " of gen closure",
|
||||
hir::CoroutineSource::Fn => {
|
||||
let parent_item =
|
||||
hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
let output = &parent_item
|
||||
.fn_decl()
|
||||
.expect("coroutine lowered from gen fn should be in fn")
|
||||
@ -712,6 +715,21 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
" of gen function"
|
||||
}
|
||||
},
|
||||
|
||||
Some(hir::CoroutineKind::AsyncGen(src)) => match src {
|
||||
hir::CoroutineSource::Block => " of async gen block",
|
||||
hir::CoroutineSource::Closure => " of async gen closure",
|
||||
hir::CoroutineSource::Fn => {
|
||||
let parent_item =
|
||||
tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
let output = &parent_item
|
||||
.fn_decl()
|
||||
.expect("coroutine lowered from async gen fn should be in fn")
|
||||
.output;
|
||||
span = output.span();
|
||||
" of async gen function"
|
||||
}
|
||||
},
|
||||
Some(hir::CoroutineKind::Coroutine) => " of coroutine",
|
||||
None => " of closure",
|
||||
};
|
||||
@ -770,28 +788,18 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
};
|
||||
let opaque_ty = hir.item(id);
|
||||
if let hir::ItemKind::OpaqueTy(hir::OpaqueTy {
|
||||
bounds:
|
||||
[
|
||||
hir::GenericBound::LangItemTrait(
|
||||
hir::LangItem::Future,
|
||||
_,
|
||||
_,
|
||||
hir::GenericArgs {
|
||||
bindings:
|
||||
[
|
||||
hir::TypeBinding {
|
||||
ident: Ident { name: sym::Output, .. },
|
||||
kind:
|
||||
hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) },
|
||||
..
|
||||
},
|
||||
],
|
||||
..
|
||||
},
|
||||
),
|
||||
],
|
||||
bounds: [hir::GenericBound::Trait(trait_ref, _)],
|
||||
..
|
||||
}) = opaque_ty.kind
|
||||
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
|
||||
&& let Some(args) = segment.args
|
||||
&& let [
|
||||
hir::TypeBinding {
|
||||
ident: Ident { name: sym::Output, .. },
|
||||
kind: hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) },
|
||||
..
|
||||
},
|
||||
] = args.bindings
|
||||
{
|
||||
ty
|
||||
} else {
|
||||
@ -823,7 +831,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
let type_name =
|
||||
self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name;
|
||||
|
||||
let yield_span = match tcx.hir().get(self.mir_hir_id()) {
|
||||
let yield_span = match tcx.hir_node(self.mir_hir_id()) {
|
||||
hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
|
||||
..
|
||||
@ -847,7 +855,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
&self,
|
||||
fr: RegionVid,
|
||||
) -> Option<RegionName> {
|
||||
let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else {
|
||||
let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else {
|
||||
return None;
|
||||
};
|
||||
if region.has_name() {
|
||||
@ -862,7 +870,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
|
||||
let found = tcx
|
||||
.any_free_region_meets(&tcx.type_of(region_parent).instantiate_identity(), |r| {
|
||||
*r == ty::ReEarlyBound(region)
|
||||
*r == ty::ReEarlyParam(region)
|
||||
});
|
||||
|
||||
Some(RegionName {
|
||||
@ -881,7 +889,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
&self,
|
||||
fr: RegionVid,
|
||||
) -> Option<RegionName> {
|
||||
let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else {
|
||||
let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else {
|
||||
return None;
|
||||
};
|
||||
if region.has_name() {
|
||||
@ -943,7 +951,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
&self,
|
||||
clauses: &[ty::Clause<'tcx>],
|
||||
ty: Ty<'tcx>,
|
||||
region: ty::EarlyBoundRegion,
|
||||
region: ty::EarlyParamRegion,
|
||||
) -> bool {
|
||||
let tcx = self.infcx.tcx;
|
||||
ty.walk().any(|arg| {
|
||||
@ -956,7 +964,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
ty::ClauseKind::Projection(data) if data.projection_ty.self_ty() == ty => {}
|
||||
_ => return false,
|
||||
}
|
||||
tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyBound(region))
|
||||
tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyParam(region))
|
||||
})
|
||||
} else {
|
||||
false
|
||||
|
||||
@ -2,10 +2,9 @@
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
||||
use crate::region_infer::RegionInferenceContext;
|
||||
use crate::Upvar;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_middle::mir::{Body, Local};
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_middle::ty::{self, RegionVid, TyCtxt};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -15,7 +14,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
local_names: &IndexSlice<Local, Option<Symbol>>,
|
||||
upvars: &[Upvar<'tcx>],
|
||||
upvars: &[&ty::CapturedPlace<'tcx>],
|
||||
fr: RegionVid,
|
||||
) -> Option<(Option<Symbol>, Span)> {
|
||||
debug!("get_var_name_and_span_for_region(fr={fr:?})");
|
||||
@ -66,10 +65,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
pub(crate) fn get_upvar_name_and_span_for_region(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
upvars: &[Upvar<'tcx>],
|
||||
upvars: &[&ty::CapturedPlace<'tcx>],
|
||||
upvar_index: usize,
|
||||
) -> (Symbol, Span) {
|
||||
let upvar_hir_id = upvars[upvar_index].place.get_root_variable();
|
||||
let upvar_hir_id = upvars[upvar_index].get_root_variable();
|
||||
debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}");
|
||||
|
||||
let upvar_name = tcx.hir().name(upvar_hir_id);
|
||||
|
||||
@ -1,441 +0,0 @@
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::{self, BasicBlock, Body, Location, NonDivergingIntrinsic, Place, Rvalue};
|
||||
use rustc_middle::mir::{BorrowKind, Mutability, Operand};
|
||||
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
|
||||
use rustc_middle::mir::{Statement, StatementKind};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
use crate::{
|
||||
borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, path_utils::*, AccessDepth,
|
||||
Activation, ArtificialField, BorrowIndex, Deep, LocalMutationIsAllowed, Read, ReadKind,
|
||||
ReadOrWrite, Reservation, Shallow, Write, WriteKind,
|
||||
};
|
||||
|
||||
pub(super) fn generate_invalidates<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
if all_facts.is_none() {
|
||||
// Nothing to do if we don't have any facts
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(all_facts) = all_facts {
|
||||
let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
|
||||
let dominators = body.basic_blocks.dominators();
|
||||
let mut ig = InvalidationGenerator {
|
||||
all_facts,
|
||||
borrow_set,
|
||||
tcx,
|
||||
location_table,
|
||||
body: &body,
|
||||
dominators,
|
||||
};
|
||||
ig.visit_body(body);
|
||||
}
|
||||
}
|
||||
|
||||
struct InvalidationGenerator<'cx, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &'cx mut AllFacts,
|
||||
location_table: &'cx LocationTable,
|
||||
body: &'cx Body<'tcx>,
|
||||
dominators: &'cx Dominators<BasicBlock>,
|
||||
borrow_set: &'cx BorrowSet<'tcx>,
|
||||
}
|
||||
|
||||
/// Visits the whole MIR and generates `invalidates()` facts.
|
||||
/// Most of the code implementing this was stolen from `borrow_check/mod.rs`.
|
||||
impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
self.check_activations(location);
|
||||
|
||||
match &statement.kind {
|
||||
StatementKind::Assign(box (lhs, rhs)) => {
|
||||
self.consume_rvalue(location, rhs);
|
||||
|
||||
self.mutate_place(location, *lhs, Shallow(None));
|
||||
}
|
||||
StatementKind::FakeRead(box (_, _)) => {
|
||||
// Only relevant for initialized/liveness/safety checks.
|
||||
}
|
||||
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => {
|
||||
self.consume_operand(location, op);
|
||||
}
|
||||
StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping {
|
||||
src,
|
||||
dst,
|
||||
count,
|
||||
})) => {
|
||||
self.consume_operand(location, src);
|
||||
self.consume_operand(location, dst);
|
||||
self.consume_operand(location, count);
|
||||
}
|
||||
// Only relevant for mir typeck
|
||||
StatementKind::AscribeUserType(..)
|
||||
// Only relevant for liveness and unsafeck
|
||||
| StatementKind::PlaceMention(..)
|
||||
// Doesn't have any language semantics
|
||||
| StatementKind::Coverage(..)
|
||||
// Does not actually affect borrowck
|
||||
| StatementKind::StorageLive(..) => {}
|
||||
StatementKind::StorageDead(local) => {
|
||||
self.access_place(
|
||||
location,
|
||||
Place::from(*local),
|
||||
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop
|
||||
| StatementKind::Retag { .. }
|
||||
| StatementKind::Deinit(..)
|
||||
| StatementKind::SetDiscriminant { .. } => {
|
||||
bug!("Statement not allowed in this MIR phase")
|
||||
}
|
||||
}
|
||||
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
self.check_activations(location);
|
||||
|
||||
match &terminator.kind {
|
||||
TerminatorKind::SwitchInt { discr, targets: _ } => {
|
||||
self.consume_operand(location, discr);
|
||||
}
|
||||
TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => {
|
||||
let write_kind =
|
||||
if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop };
|
||||
self.access_place(
|
||||
location,
|
||||
*drop_place,
|
||||
(AccessDepth::Drop, Write(write_kind)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
TerminatorKind::Call {
|
||||
func,
|
||||
args,
|
||||
destination,
|
||||
target: _,
|
||||
unwind: _,
|
||||
call_source: _,
|
||||
fn_span: _,
|
||||
} => {
|
||||
self.consume_operand(location, func);
|
||||
for arg in args {
|
||||
self.consume_operand(location, arg);
|
||||
}
|
||||
self.mutate_place(location, *destination, Deep);
|
||||
}
|
||||
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
|
||||
self.consume_operand(location, cond);
|
||||
use rustc_middle::mir::AssertKind;
|
||||
if let AssertKind::BoundsCheck { len, index } = &**msg {
|
||||
self.consume_operand(location, len);
|
||||
self.consume_operand(location, index);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Yield { value, resume, resume_arg, drop: _ } => {
|
||||
self.consume_operand(location, value);
|
||||
|
||||
// Invalidate all borrows of local places
|
||||
let borrow_set = self.borrow_set;
|
||||
let resume = self.location_table.start_index(resume.start_location());
|
||||
for (i, data) in borrow_set.iter_enumerated() {
|
||||
if borrow_of_local_data(data.borrowed_place) {
|
||||
self.all_facts.loan_invalidated_at.push((resume, i));
|
||||
}
|
||||
}
|
||||
|
||||
self.mutate_place(location, *resume_arg, Deep);
|
||||
}
|
||||
TerminatorKind::UnwindResume
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::CoroutineDrop => {
|
||||
// Invalidate all borrows of local places
|
||||
let borrow_set = self.borrow_set;
|
||||
let start = self.location_table.start_index(location);
|
||||
for (i, data) in borrow_set.iter_enumerated() {
|
||||
if borrow_of_local_data(data.borrowed_place) {
|
||||
self.all_facts.loan_invalidated_at.push((start, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
TerminatorKind::InlineAsm {
|
||||
template: _,
|
||||
operands,
|
||||
options: _,
|
||||
line_spans: _,
|
||||
destination: _,
|
||||
unwind: _,
|
||||
} => {
|
||||
for op in operands {
|
||||
match op {
|
||||
InlineAsmOperand::In { reg: _, value } => {
|
||||
self.consume_operand(location, value);
|
||||
}
|
||||
InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
|
||||
if let &Some(place) = place {
|
||||
self.mutate_place(location, place, Shallow(None));
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::InOut { reg: _, late: _, in_value, out_place } => {
|
||||
self.consume_operand(location, in_value);
|
||||
if let &Some(out_place) = out_place {
|
||||
self.mutate_place(location, out_place, Shallow(None));
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::Const { value: _ }
|
||||
| InlineAsmOperand::SymFn { value: _ }
|
||||
| InlineAsmOperand::SymStatic { def_id: _ } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
TerminatorKind::Goto { target: _ }
|
||||
| TerminatorKind::UnwindTerminate(_)
|
||||
| TerminatorKind::Unreachable
|
||||
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
|
||||
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
|
||||
// no data used, thus irrelevant to borrowck
|
||||
}
|
||||
}
|
||||
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
|
||||
/// Simulates mutation of a place.
|
||||
fn mutate_place(&mut self, location: Location, place: Place<'tcx>, kind: AccessDepth) {
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(kind, Write(WriteKind::Mutate)),
|
||||
LocalMutationIsAllowed::ExceptUpvars,
|
||||
);
|
||||
}
|
||||
|
||||
/// Simulates consumption of an operand.
|
||||
fn consume_operand(&mut self, location: Location, operand: &Operand<'tcx>) {
|
||||
match *operand {
|
||||
Operand::Copy(place) => {
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(Deep, Read(ReadKind::Copy)),
|
||||
LocalMutationIsAllowed::No,
|
||||
);
|
||||
}
|
||||
Operand::Move(place) => {
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(Deep, Write(WriteKind::Move)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
Operand::Constant(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Simulates consumption of an rvalue
|
||||
fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) {
|
||||
match rvalue {
|
||||
&Rvalue::Ref(_ /*rgn*/, bk, place) => {
|
||||
let access_kind = match bk {
|
||||
BorrowKind::Fake => {
|
||||
(Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
|
||||
}
|
||||
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
|
||||
BorrowKind::Mut { .. } => {
|
||||
let wk = WriteKind::MutableBorrow(bk);
|
||||
if allow_two_phase_borrow(bk) {
|
||||
(Deep, Reservation(wk))
|
||||
} else {
|
||||
(Deep, Write(wk))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
|
||||
}
|
||||
|
||||
&Rvalue::AddressOf(mutability, place) => {
|
||||
let access_kind = match mutability {
|
||||
Mutability::Mut => (
|
||||
Deep,
|
||||
Write(WriteKind::MutableBorrow(BorrowKind::Mut {
|
||||
kind: mir::MutBorrowKind::Default,
|
||||
})),
|
||||
),
|
||||
Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
|
||||
};
|
||||
|
||||
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
|
||||
}
|
||||
|
||||
Rvalue::ThreadLocalRef(_) => {}
|
||||
|
||||
Rvalue::Use(operand)
|
||||
| Rvalue::Repeat(operand, _)
|
||||
| Rvalue::UnaryOp(_ /*un_op*/, operand)
|
||||
| Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/)
|
||||
| Rvalue::ShallowInitBox(operand, _ /*ty*/) => self.consume_operand(location, operand),
|
||||
|
||||
&Rvalue::CopyForDeref(place) => {
|
||||
let op = &Operand::Copy(place);
|
||||
self.consume_operand(location, op);
|
||||
}
|
||||
|
||||
&(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
|
||||
let af = match rvalue {
|
||||
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
|
||||
Rvalue::Discriminant(..) => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(Shallow(af), Read(ReadKind::Copy)),
|
||||
LocalMutationIsAllowed::No,
|
||||
);
|
||||
}
|
||||
|
||||
Rvalue::BinaryOp(_bin_op, box (operand1, operand2))
|
||||
| Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => {
|
||||
self.consume_operand(location, operand1);
|
||||
self.consume_operand(location, operand2);
|
||||
}
|
||||
|
||||
Rvalue::NullaryOp(_op, _ty) => {}
|
||||
|
||||
Rvalue::Aggregate(_, operands) => {
|
||||
for operand in operands {
|
||||
self.consume_operand(location, operand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simulates an access to a place.
|
||||
fn access_place(
|
||||
&mut self,
|
||||
location: Location,
|
||||
place: Place<'tcx>,
|
||||
kind: (AccessDepth, ReadOrWrite),
|
||||
_is_local_mutation_allowed: LocalMutationIsAllowed,
|
||||
) {
|
||||
let (sd, rw) = kind;
|
||||
// note: not doing check_access_permissions checks because they don't generate invalidates
|
||||
self.check_access_for_conflict(location, place, sd, rw);
|
||||
}
|
||||
|
||||
fn check_access_for_conflict(
|
||||
&mut self,
|
||||
location: Location,
|
||||
place: Place<'tcx>,
|
||||
sd: AccessDepth,
|
||||
rw: ReadOrWrite,
|
||||
) {
|
||||
debug!(
|
||||
"invalidation::check_access_for_conflict(location={:?}, place={:?}, sd={:?}, \
|
||||
rw={:?})",
|
||||
location, place, sd, rw,
|
||||
);
|
||||
let tcx = self.tcx;
|
||||
let body = self.body;
|
||||
let borrow_set = self.borrow_set;
|
||||
each_borrow_involving_path(
|
||||
self,
|
||||
tcx,
|
||||
body,
|
||||
location,
|
||||
(sd, place),
|
||||
borrow_set,
|
||||
|_| true,
|
||||
|this, borrow_index, borrow| {
|
||||
match (rw, borrow.kind) {
|
||||
// Obviously an activation is compatible with its own
|
||||
// reservation (or even prior activating uses of same
|
||||
// borrow); so don't check if they interfere.
|
||||
//
|
||||
// NOTE: *reservations* do conflict with themselves;
|
||||
// thus aren't injecting unsoundness w/ this check.)
|
||||
(Activation(_, activating), _) if activating == borrow_index => {
|
||||
// Activating a borrow doesn't generate any invalidations, since we
|
||||
// have already taken the reservation
|
||||
}
|
||||
|
||||
(Read(_), BorrowKind::Fake | BorrowKind::Shared)
|
||||
| (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => {
|
||||
// Reads don't invalidate shared or shallow borrows
|
||||
}
|
||||
|
||||
(Read(_), BorrowKind::Mut { .. }) => {
|
||||
// Reading from mere reservations of mutable-borrows is OK.
|
||||
if !is_active(&this.dominators, borrow, location) {
|
||||
// If the borrow isn't active yet, reads don't invalidate it
|
||||
assert!(allow_two_phase_borrow(borrow.kind));
|
||||
return Control::Continue;
|
||||
}
|
||||
|
||||
// Unique and mutable borrows are invalidated by reads from any
|
||||
// involved path
|
||||
this.emit_loan_invalidated_at(borrow_index, location);
|
||||
}
|
||||
|
||||
(Reservation(_) | Activation(_, _) | Write(_), _) => {
|
||||
// unique or mutable borrows are invalidated by writes.
|
||||
// Reservations count as writes since we need to check
|
||||
// that activating the borrow will be OK
|
||||
// FIXME(bob_twinkles) is this actually the right thing to do?
|
||||
this.emit_loan_invalidated_at(borrow_index, location);
|
||||
}
|
||||
}
|
||||
Control::Continue
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Generates a new `loan_invalidated_at(L, B)` fact.
|
||||
fn emit_loan_invalidated_at(&mut self, b: BorrowIndex, l: Location) {
|
||||
let lidx = self.location_table.start_index(l);
|
||||
self.all_facts.loan_invalidated_at.push((lidx, b));
|
||||
}
|
||||
|
||||
fn check_activations(&mut self, location: Location) {
|
||||
// Two-phase borrow support: For each activation that is newly
|
||||
// generated at this statement, check if it interferes with
|
||||
// another borrow.
|
||||
for &borrow_index in self.borrow_set.activations_at_location(location) {
|
||||
let borrow = &self.borrow_set[borrow_index];
|
||||
|
||||
// only mutable borrows should be 2-phase
|
||||
assert!(match borrow.kind {
|
||||
BorrowKind::Shared | BorrowKind::Fake => false,
|
||||
BorrowKind::Mut { .. } => true,
|
||||
});
|
||||
|
||||
self.access_place(
|
||||
location,
|
||||
borrow.borrowed_place,
|
||||
(Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)),
|
||||
LocalMutationIsAllowed::No,
|
||||
);
|
||||
|
||||
// We do not need to call `check_if_path_or_subpath_is_moved`
|
||||
// again, as we already called it when we made the
|
||||
// initial reservation.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
//! This query borrow-checks the MIR to (further) ensure it is not broken.
|
||||
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(let_chains)]
|
||||
@ -22,8 +22,7 @@ extern crate tracing;
|
||||
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticMessage, SubdiagnosticMessage};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
use rustc_errors::{Diagnostic, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
|
||||
@ -35,7 +34,7 @@ use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::traits::DefiningAnchor;
|
||||
use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
|
||||
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt};
|
||||
use rustc_session::lint::builtin::UNUSED_MUT;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
@ -43,6 +42,7 @@ use rustc_target::abi::FieldIdx;
|
||||
use smallvec::SmallVec;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -65,19 +65,18 @@ use self::path_utils::*;
|
||||
|
||||
pub mod borrow_set;
|
||||
mod borrowck_errors;
|
||||
mod constraint_generation;
|
||||
mod constraints;
|
||||
mod dataflow;
|
||||
mod def_use;
|
||||
mod diagnostics;
|
||||
mod facts;
|
||||
mod invalidation;
|
||||
mod location;
|
||||
mod member_constraints;
|
||||
mod nll;
|
||||
mod path_utils;
|
||||
mod place_ext;
|
||||
mod places_conflict;
|
||||
mod polonius;
|
||||
mod prefixes;
|
||||
mod region_infer;
|
||||
mod renumber;
|
||||
@ -98,19 +97,10 @@ use places_conflict::{places_conflict, PlaceConflictBias};
|
||||
use region_infer::RegionInferenceContext;
|
||||
use renumber::RegionCtxt;
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
|
||||
// FIXME(eddyb) perhaps move this somewhere more centrally.
|
||||
#[derive(Debug)]
|
||||
struct Upvar<'tcx> {
|
||||
place: CapturedPlace<'tcx>,
|
||||
|
||||
/// If true, the capture is behind a reference.
|
||||
by_ref: bool,
|
||||
}
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
/// Associate some local constants with the `'tcx` lifetime
|
||||
struct TyCtxtConsts<'tcx>(TyCtxt<'tcx>);
|
||||
struct TyCtxtConsts<'tcx>(PhantomData<&'tcx ()>);
|
||||
impl<'tcx> TyCtxtConsts<'tcx> {
|
||||
const DEREF_PROJECTION: &'tcx [PlaceElem<'tcx>; 1] = &[ProjectionElem::Deref];
|
||||
}
|
||||
@ -135,7 +125,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
|
||||
return tcx.arena.alloc(result);
|
||||
}
|
||||
|
||||
let hir_owner = tcx.hir().local_def_id_to_hir_id(def).owner;
|
||||
let hir_owner = tcx.local_def_id_to_hir_id(def).owner;
|
||||
|
||||
let infcx =
|
||||
tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)).build();
|
||||
@ -193,18 +183,6 @@ fn do_mir_borrowck<'tcx>(
|
||||
infcx.set_tainted_by_errors(e);
|
||||
errors.set_tainted_by_errors(e);
|
||||
}
|
||||
let upvars: Vec<_> = tcx
|
||||
.closure_captures(def)
|
||||
.iter()
|
||||
.map(|&captured_place| {
|
||||
let capture = captured_place.info.capture_kind;
|
||||
let by_ref = match capture {
|
||||
ty::UpvarCapture::ByValue => false,
|
||||
ty::UpvarCapture::ByRef(..) => true,
|
||||
};
|
||||
Upvar { place: captured_place.clone(), by_ref }
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Replace all regions with fresh inference variables. This
|
||||
// requires first making our own copy of the MIR. This copy will
|
||||
@ -216,21 +194,20 @@ fn do_mir_borrowck<'tcx>(
|
||||
nll::replace_regions_in_mir(&infcx, param_env, &mut body_owned, &mut promoted);
|
||||
let body = &body_owned; // no further changes
|
||||
|
||||
let location_table_owned = LocationTable::new(body);
|
||||
let location_table = &location_table_owned;
|
||||
let location_table = LocationTable::new(body);
|
||||
|
||||
let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true);
|
||||
let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true);
|
||||
let promoted_move_data = promoted
|
||||
.iter_enumerated()
|
||||
.map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env, |_| true)));
|
||||
.map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, param_env, |_| true)));
|
||||
|
||||
let mdpe = MoveDataParamEnv { move_data, param_env };
|
||||
|
||||
let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe)
|
||||
.into_engine(tcx, &body)
|
||||
let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
|
||||
.into_engine(tcx, body)
|
||||
.pass_name("borrowck")
|
||||
.iterate_to_fixpoint()
|
||||
.into_results_cursor(&body);
|
||||
.into_results_cursor(body);
|
||||
|
||||
let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(def).is_fn_or_closure();
|
||||
let borrow_set =
|
||||
@ -249,24 +226,24 @@ fn do_mir_borrowck<'tcx>(
|
||||
free_regions,
|
||||
body,
|
||||
&promoted,
|
||||
location_table,
|
||||
&location_table,
|
||||
param_env,
|
||||
&mut flow_inits,
|
||||
&mdpe.move_data,
|
||||
&borrow_set,
|
||||
&upvars,
|
||||
tcx.closure_captures(def),
|
||||
consumer_options,
|
||||
);
|
||||
|
||||
// Dump MIR results into a file, if that is enabled. This let us
|
||||
// write unit-tests, as well as helping with debugging.
|
||||
nll::dump_mir_results(&infcx, &body, ®ioncx, &opt_closure_req);
|
||||
nll::dump_mir_results(&infcx, body, ®ioncx, &opt_closure_req);
|
||||
|
||||
// We also have a `#[rustc_regions]` annotation that causes us to dump
|
||||
// information.
|
||||
nll::dump_annotation(
|
||||
&infcx,
|
||||
&body,
|
||||
body,
|
||||
®ioncx,
|
||||
&opt_closure_req,
|
||||
&opaque_type_values,
|
||||
@ -288,7 +265,7 @@ fn do_mir_borrowck<'tcx>(
|
||||
.into_engine(tcx, body)
|
||||
.pass_name("borrowck")
|
||||
.iterate_to_fixpoint();
|
||||
let flow_ever_inits = EverInitializedPlaces::new(tcx, body, &mdpe)
|
||||
let flow_ever_inits = EverInitializedPlaces::new(body, &mdpe)
|
||||
.into_engine(tcx, body)
|
||||
.pass_name("borrowck")
|
||||
.iterate_to_fixpoint();
|
||||
@ -313,7 +290,7 @@ fn do_mir_borrowck<'tcx>(
|
||||
param_env,
|
||||
body: promoted_body,
|
||||
move_data: &move_data,
|
||||
location_table, // no need to create a real one for the promoted, it is not used
|
||||
location_table: &location_table, // no need to create a real one for the promoted, it is not used
|
||||
movable_coroutine,
|
||||
fn_self_span_reported: Default::default(),
|
||||
locals_are_invalidated_at_exit,
|
||||
@ -324,7 +301,7 @@ fn do_mir_borrowck<'tcx>(
|
||||
used_mut: Default::default(),
|
||||
used_mut_upvars: SmallVec::new(),
|
||||
borrow_set: Rc::clone(&borrow_set),
|
||||
upvars: Vec::new(),
|
||||
upvars: &[],
|
||||
local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
|
||||
region_names: RefCell::default(),
|
||||
next_region_name: RefCell::new(1),
|
||||
@ -354,7 +331,7 @@ fn do_mir_borrowck<'tcx>(
|
||||
param_env,
|
||||
body,
|
||||
move_data: &mdpe.move_data,
|
||||
location_table,
|
||||
location_table: &location_table,
|
||||
movable_coroutine,
|
||||
locals_are_invalidated_at_exit,
|
||||
fn_self_span_reported: Default::default(),
|
||||
@ -365,7 +342,7 @@ fn do_mir_borrowck<'tcx>(
|
||||
used_mut: Default::default(),
|
||||
used_mut_upvars: SmallVec::new(),
|
||||
borrow_set: Rc::clone(&borrow_set),
|
||||
upvars,
|
||||
upvars: tcx.closure_captures(def),
|
||||
local_names,
|
||||
region_names: RefCell::default(),
|
||||
next_region_name: RefCell::new(1),
|
||||
@ -456,7 +433,7 @@ fn do_mir_borrowck<'tcx>(
|
||||
promoted,
|
||||
borrow_set,
|
||||
region_inference_context: regioncx,
|
||||
location_table: polonius_input.as_ref().map(|_| location_table_owned),
|
||||
location_table: polonius_input.as_ref().map(|_| location_table),
|
||||
input_facts: polonius_input,
|
||||
output_facts,
|
||||
}))
|
||||
@ -584,7 +561,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
|
||||
borrow_set: Rc<BorrowSet<'tcx>>,
|
||||
|
||||
/// Information about upvars not necessarily preserved in types or MIR
|
||||
upvars: Vec<Upvar<'tcx>>,
|
||||
upvars: &'tcx [&'tcx ty::CapturedPlace<'tcx>],
|
||||
|
||||
/// Names of local (user) variables (extracted from `var_debug_info`).
|
||||
local_names: IndexVec<Local, Option<Symbol>>,
|
||||
@ -1041,9 +1018,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
flow_state: &Flows<'cx, 'tcx>,
|
||||
) -> bool {
|
||||
let mut error_reported = false;
|
||||
let tcx = self.infcx.tcx;
|
||||
let body = self.body;
|
||||
let borrow_set = self.borrow_set.clone();
|
||||
let borrow_set = Rc::clone(&self.borrow_set);
|
||||
|
||||
// Use polonius output if it has been enabled.
|
||||
let mut polonius_output;
|
||||
@ -1060,8 +1035,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
each_borrow_involving_path(
|
||||
self,
|
||||
tcx,
|
||||
body,
|
||||
self.infcx.tcx,
|
||||
self.body,
|
||||
location,
|
||||
(sd, place_span.0),
|
||||
&borrow_set,
|
||||
@ -1538,7 +1513,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
if places_conflict::borrow_conflicts_with_place(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
self.body,
|
||||
place,
|
||||
borrow.kind,
|
||||
root_place,
|
||||
@ -2155,11 +2130,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
&& !self.has_buffered_errors()
|
||||
{
|
||||
// rust-lang/rust#46908: In pure NLL mode this code path should be
|
||||
// unreachable, but we use `delay_span_bug` because we can hit this when
|
||||
// unreachable, but we use `span_delayed_bug` because we can hit this when
|
||||
// dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug`
|
||||
// enabled. We don't want to ICE for that case, as other errors will have
|
||||
// been emitted (#52262).
|
||||
self.infcx.tcx.sess.delay_span_bug(
|
||||
self.infcx.tcx.sess.span_delayed_bug(
|
||||
span,
|
||||
format!(
|
||||
"Accessing `{place:?}` with the kind `{kind:?}` shouldn't be possible",
|
||||
@ -2193,7 +2168,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// If this is a mutate access to an immutable local variable with no projections
|
||||
// report the error as an illegal reassignment
|
||||
let init = &self.move_data.inits[init_index];
|
||||
let assigned_span = init.span(&self.body);
|
||||
let assigned_span = init.span(self.body);
|
||||
self.report_illegal_reassignment(location, (place, span), assigned_span, place);
|
||||
} else {
|
||||
self.report_mutability_error(place, span, the_place_err, error_access, location)
|
||||
@ -2294,7 +2269,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// unique path to the `&mut`
|
||||
hir::Mutability::Mut => {
|
||||
let mode = match self.is_upvar_field_projection(place) {
|
||||
Some(field) if self.upvars[field.index()].by_ref => {
|
||||
Some(field)
|
||||
if self.upvars[field.index()].is_by_ref() =>
|
||||
{
|
||||
is_local_mutation_allowed
|
||||
}
|
||||
_ => LocalMutationIsAllowed::Yes,
|
||||
@ -2342,7 +2319,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
place={:?}, place_base={:?}",
|
||||
upvar, is_local_mutation_allowed, place, place_base
|
||||
);
|
||||
match (upvar.place.mutability, is_local_mutation_allowed) {
|
||||
match (upvar.mutability, is_local_mutation_allowed) {
|
||||
(
|
||||
Mutability::Not,
|
||||
LocalMutationIsAllowed::No
|
||||
@ -2451,7 +2428,7 @@ mod error {
|
||||
|
||||
pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) {
|
||||
if let None = self.tainted_by_errors {
|
||||
self.tainted_by_errors = Some(self.tcx.sess.delay_span_bug(
|
||||
self.tainted_by_errors = Some(self.tcx.sess.span_delayed_bug(
|
||||
t.span.clone_ignoring_labels(),
|
||||
"diagnostic buffered but not emitted",
|
||||
))
|
||||
@ -2525,8 +2502,8 @@ mod error {
|
||||
if !self.errors.buffered.is_empty() {
|
||||
self.errors.buffered.sort_by_key(|diag| diag.sort_span);
|
||||
|
||||
for mut diag in self.errors.buffered.drain(..) {
|
||||
self.infcx.tcx.sess.diagnostic().emit_diagnostic(&mut diag);
|
||||
for diag in self.errors.buffered.drain(..) {
|
||||
self.infcx.tcx.sess.dcx().emit_diagnostic(diag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ pub struct LocationTable {
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[orderable]
|
||||
#[debug_format = "LocationIndex({})"]
|
||||
pub struct LocationIndex {}
|
||||
}
|
||||
|
||||
@ -2,16 +2,17 @@
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
//! The entry point of the NLL borrow checker.
|
||||
|
||||
use polonius_engine::{Algorithm, Output};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere};
|
||||
use rustc_middle::mir::{
|
||||
Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted,
|
||||
START_BLOCK,
|
||||
};
|
||||
use rustc_middle::mir::{Body, ClosureOutlivesSubject, ClosureRegionRequirements, Promoted};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt};
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
use rustc_span::symbol::sym;
|
||||
use std::env;
|
||||
use std::io;
|
||||
@ -19,25 +20,18 @@ use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use polonius_engine::{Algorithm, Output};
|
||||
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
|
||||
use crate::{
|
||||
borrow_set::BorrowSet,
|
||||
constraint_generation,
|
||||
consumers::ConsumerOptions,
|
||||
diagnostics::RegionErrors,
|
||||
facts::{AllFacts, AllFactsExt, RustcFacts},
|
||||
invalidation,
|
||||
location::LocationTable,
|
||||
polonius,
|
||||
region_infer::{values::RegionValueElements, RegionInferenceContext},
|
||||
renumber,
|
||||
type_check::{self, MirTypeckRegionConstraints, MirTypeckResults},
|
||||
universal_regions::UniversalRegions,
|
||||
BorrowckInferCtxt, Upvar,
|
||||
BorrowckInferCtxt,
|
||||
};
|
||||
|
||||
pub type PoloniusOutput = Output<RustcFacts>;
|
||||
@ -78,81 +72,6 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
|
||||
universal_regions
|
||||
}
|
||||
|
||||
// This function populates an AllFacts instance with base facts related to
|
||||
// MovePaths and needed for the move analysis.
|
||||
fn populate_polonius_move_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
move_data: &MoveData<'_>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'_>,
|
||||
) {
|
||||
all_facts
|
||||
.path_is_var
|
||||
.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
|
||||
|
||||
for (child, move_path) in move_data.move_paths.iter_enumerated() {
|
||||
if let Some(parent) = move_path.parent {
|
||||
all_facts.child_path.push((child, parent));
|
||||
}
|
||||
}
|
||||
|
||||
let fn_entry_start =
|
||||
location_table.start_index(Location { block: START_BLOCK, statement_index: 0 });
|
||||
|
||||
// initialized_at
|
||||
for init in move_data.inits.iter() {
|
||||
match init.location {
|
||||
InitLocation::Statement(location) => {
|
||||
let block_data = &body[location.block];
|
||||
let is_terminator = location.statement_index == block_data.statements.len();
|
||||
|
||||
if is_terminator && init.kind == InitKind::NonPanicPathOnly {
|
||||
// We are at the terminator of an init that has a panic path,
|
||||
// and where the init should not happen on panic
|
||||
|
||||
for successor in block_data.terminator().successors() {
|
||||
if body[successor].is_cleanup {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The initialization happened in (or rather, when arriving at)
|
||||
// the successors, but not in the unwind block.
|
||||
let first_statement = Location { block: successor, statement_index: 0 };
|
||||
all_facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.start_index(first_statement)));
|
||||
}
|
||||
} else {
|
||||
// In all other cases, the initialization just happens at the
|
||||
// midpoint, like any other effect.
|
||||
all_facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.mid_index(location)));
|
||||
}
|
||||
}
|
||||
// Arguments are initialized on function entry
|
||||
InitLocation::Argument(local) => {
|
||||
assert!(body.local_kind(local) == LocalKind::Arg);
|
||||
all_facts.path_assigned_at_base.push((init.path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (local, path) in move_data.rev_lookup.iter_locals_enumerated() {
|
||||
if body.local_kind(local) != LocalKind::Arg {
|
||||
// Non-arguments start out deinitialised; we simulate this with an
|
||||
// initial move:
|
||||
all_facts.path_moved_at_base.push((path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
|
||||
// moved_out_at
|
||||
// deinitialisation is assumed to always happen!
|
||||
all_facts
|
||||
.path_moved_at_base
|
||||
.extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))));
|
||||
}
|
||||
|
||||
/// Computes the (non-lexical) regions from the input MIR.
|
||||
///
|
||||
/// This may result in errors being reported.
|
||||
@ -166,7 +85,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
|
||||
flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
upvars: &[Upvar<'tcx>],
|
||||
upvars: &[&ty::CapturedPlace<'tcx>],
|
||||
consumer_options: Option<ConsumerOptions>,
|
||||
) -> NllOutput<'tcx> {
|
||||
let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
|
||||
@ -179,70 +98,26 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
|
||||
|
||||
let universal_regions = Rc::new(universal_regions);
|
||||
|
||||
let elements = &Rc::new(RegionValueElements::new(&body));
|
||||
let elements = &Rc::new(RegionValueElements::new(body));
|
||||
|
||||
// Run the MIR type-checker.
|
||||
let MirTypeckResults {
|
||||
constraints,
|
||||
universal_region_relations,
|
||||
opaque_type_values,
|
||||
live_loans,
|
||||
} = type_check::type_check(
|
||||
infcx,
|
||||
param_env,
|
||||
body,
|
||||
promoted,
|
||||
&universal_regions,
|
||||
location_table,
|
||||
borrow_set,
|
||||
&mut all_facts,
|
||||
flow_inits,
|
||||
move_data,
|
||||
elements,
|
||||
upvars,
|
||||
polonius_input,
|
||||
);
|
||||
|
||||
if let Some(all_facts) = &mut all_facts {
|
||||
let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
all_facts.universal_region.extend(universal_regions.universal_regions());
|
||||
populate_polonius_move_facts(all_facts, move_data, location_table, &body);
|
||||
|
||||
// Emit universal regions facts, and their relations, for Polonius.
|
||||
//
|
||||
// 1: universal regions are modeled in Polonius as a pair:
|
||||
// - the universal region vid itself.
|
||||
// - a "placeholder loan" associated to this universal region. Since they don't exist in
|
||||
// the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
|
||||
// added to the existing number of loans, as if they succeeded them in the set.
|
||||
//
|
||||
let borrow_count = borrow_set.len();
|
||||
debug!(
|
||||
"compute_regions: polonius placeholders, num_universals={}, borrow_count={}",
|
||||
universal_regions.len(),
|
||||
borrow_count
|
||||
let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } =
|
||||
type_check::type_check(
|
||||
infcx,
|
||||
param_env,
|
||||
body,
|
||||
promoted,
|
||||
&universal_regions,
|
||||
location_table,
|
||||
borrow_set,
|
||||
&mut all_facts,
|
||||
flow_inits,
|
||||
move_data,
|
||||
elements,
|
||||
upvars,
|
||||
polonius_input,
|
||||
);
|
||||
|
||||
for universal_region in universal_regions.universal_regions() {
|
||||
let universal_region_idx = universal_region.index();
|
||||
let placeholder_loan_idx = borrow_count + universal_region_idx;
|
||||
all_facts.placeholder.push((universal_region, placeholder_loan_idx.into()));
|
||||
}
|
||||
|
||||
// 2: the universal region relations `outlives` constraints are emitted as
|
||||
// `known_placeholder_subset` facts.
|
||||
for (fr1, fr2) in universal_region_relations.known_outlives() {
|
||||
if fr1 != fr2 {
|
||||
debug!(
|
||||
"compute_regions: emitting polonius `known_placeholder_subset` \
|
||||
fr1={:?}, fr2={:?}",
|
||||
fr1, fr2
|
||||
);
|
||||
all_facts.known_placeholder_subset.push((fr1, fr2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the region inference context, taking ownership of the
|
||||
// region inference data that was contained in `infcx`, and the
|
||||
// base constraints generated by the type-check.
|
||||
@ -250,7 +125,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
|
||||
let MirTypeckRegionConstraints {
|
||||
placeholder_indices,
|
||||
placeholder_index_to_region: _,
|
||||
mut liveness_constraints,
|
||||
liveness_constraints,
|
||||
outlives_constraints,
|
||||
member_constraints,
|
||||
universe_causes,
|
||||
@ -258,13 +133,16 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
|
||||
} = constraints;
|
||||
let placeholder_indices = Rc::new(placeholder_indices);
|
||||
|
||||
constraint_generation::generate_constraints(
|
||||
infcx,
|
||||
&mut liveness_constraints,
|
||||
// If requested, emit legacy polonius facts.
|
||||
polonius::emit_facts(
|
||||
&mut all_facts,
|
||||
infcx.tcx,
|
||||
location_table,
|
||||
&body,
|
||||
body,
|
||||
borrow_set,
|
||||
move_data,
|
||||
&universal_regions,
|
||||
&universal_region_relations,
|
||||
);
|
||||
|
||||
let mut regioncx = RegionInferenceContext::new(
|
||||
@ -279,17 +157,12 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
|
||||
type_tests,
|
||||
liveness_constraints,
|
||||
elements,
|
||||
live_loans,
|
||||
);
|
||||
|
||||
// Generate various additional constraints.
|
||||
invalidation::generate_invalidates(infcx.tcx, &mut all_facts, location_table, body, borrow_set);
|
||||
|
||||
let def_id = body.source.def_id();
|
||||
|
||||
// Dump facts if requested.
|
||||
// If requested: dump NLL facts, and run legacy polonius analysis.
|
||||
let polonius_output = all_facts.as_ref().and_then(|all_facts| {
|
||||
if infcx.tcx.sess.opts.unstable_opts.nll_facts {
|
||||
let def_id = body.source.def_id();
|
||||
let def_path = infcx.tcx.def_path(def_id);
|
||||
let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir)
|
||||
.join(def_path.to_filename_friendly_no_crate());
|
||||
@ -302,7 +175,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
|
||||
let algorithm = Algorithm::from_str(&algorithm).unwrap();
|
||||
debug!("compute_regions: using polonius algorithm {:?}", algorithm);
|
||||
let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis");
|
||||
Some(Rc::new(Output::compute(&all_facts, algorithm, false)))
|
||||
Some(Rc::new(Output::compute(all_facts, algorithm, false)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -310,17 +183,17 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
|
||||
|
||||
// Solve the region constraints.
|
||||
let (closure_region_requirements, nll_errors) =
|
||||
regioncx.solve(infcx, param_env, &body, polonius_output.clone());
|
||||
regioncx.solve(infcx, param_env, body, polonius_output.clone());
|
||||
|
||||
if !nll_errors.is_empty() {
|
||||
// Suppress unhelpful extra errors in `infer_opaque_types`.
|
||||
infcx.set_tainted_by_errors(infcx.tcx.sess.delay_span_bug(
|
||||
infcx.set_tainted_by_errors(infcx.tcx.sess.span_delayed_bug(
|
||||
body.span,
|
||||
"`compute_regions` tainted `infcx` with errors but did not emit any errors",
|
||||
));
|
||||
}
|
||||
|
||||
let remapped_opaque_tys = regioncx.infer_opaque_types(&infcx, opaque_type_values);
|
||||
let remapped_opaque_tys = regioncx.infer_opaque_types(infcx, opaque_type_values);
|
||||
|
||||
NllOutput {
|
||||
regioncx,
|
||||
@ -407,7 +280,7 @@ pub(super) fn dump_annotation<'tcx>(
|
||||
|
||||
let def_span = tcx.def_span(body.source.def_id());
|
||||
let mut err = if let Some(closure_region_requirements) = closure_region_requirements {
|
||||
let mut err = tcx.sess.diagnostic().span_note_diag(def_span, "external requirements");
|
||||
let mut err = tcx.sess.dcx().struct_span_note(def_span, "external requirements");
|
||||
|
||||
regioncx.annotate(tcx, &mut err);
|
||||
|
||||
@ -426,7 +299,7 @@ pub(super) fn dump_annotation<'tcx>(
|
||||
|
||||
err
|
||||
} else {
|
||||
let mut err = tcx.sess.diagnostic().span_note_diag(def_span, "no external requirements");
|
||||
let mut err = tcx.sess.dcx().struct_span_note(def_span, "no external requirements");
|
||||
regioncx.annotate(tcx, &mut err);
|
||||
|
||||
err
|
||||
|
||||
@ -4,7 +4,6 @@ use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
|
||||
use crate::places_conflict;
|
||||
use crate::AccessDepth;
|
||||
use crate::BorrowIndex;
|
||||
use crate::Upvar;
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_middle::mir::BorrowKind;
|
||||
use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem};
|
||||
@ -150,7 +149,7 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
|
||||
/// of a closure type.
|
||||
pub(crate) fn is_upvar_field_projection<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
upvars: &[Upvar<'tcx>],
|
||||
upvars: &[&rustc_middle::ty::CapturedPlace<'tcx>],
|
||||
place_ref: PlaceRef<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
) -> Option<FieldIdx> {
|
||||
@ -166,7 +165,7 @@ pub(crate) fn is_upvar_field_projection<'tcx>(
|
||||
Some((place_base, ProjectionElem::Field(field, _ty))) => {
|
||||
let base_ty = place_base.ty(body, tcx).ty;
|
||||
if (base_ty.is_closure() || base_ty.is_coroutine())
|
||||
&& (!by_ref || upvars[field.index()].by_ref)
|
||||
&& (!by_ref || upvars[field.index()].is_by_ref())
|
||||
{
|
||||
Some(field)
|
||||
} else {
|
||||
|
||||
424
compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
Normal file
424
compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
Normal file
@ -0,0 +1,424 @@
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::{self, BasicBlock, Body, Location, NonDivergingIntrinsic, Place, Rvalue};
|
||||
use rustc_middle::mir::{BorrowKind, Mutability, Operand};
|
||||
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
|
||||
use rustc_middle::mir::{Statement, StatementKind};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
use crate::{
|
||||
borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, path_utils::*, AccessDepth,
|
||||
Activation, ArtificialField, BorrowIndex, Deep, LocalMutationIsAllowed, Read, ReadKind,
|
||||
ReadOrWrite, Reservation, Shallow, Write, WriteKind,
|
||||
};
|
||||
|
||||
/// Emit `loan_invalidated_at` facts.
|
||||
pub(super) fn emit_loan_invalidations<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &mut AllFacts,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
let dominators = body.basic_blocks.dominators();
|
||||
let mut visitor =
|
||||
LoanInvalidationsGenerator { all_facts, borrow_set, tcx, location_table, body, dominators };
|
||||
visitor.visit_body(body);
|
||||
}
|
||||
|
||||
struct LoanInvalidationsGenerator<'cx, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &'cx mut AllFacts,
|
||||
location_table: &'cx LocationTable,
|
||||
body: &'cx Body<'tcx>,
|
||||
dominators: &'cx Dominators<BasicBlock>,
|
||||
borrow_set: &'cx BorrowSet<'tcx>,
|
||||
}
|
||||
|
||||
/// Visits the whole MIR and generates `invalidates()` facts.
|
||||
/// Most of the code implementing this was stolen from `borrow_check/mod.rs`.
|
||||
impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
self.check_activations(location);
|
||||
|
||||
match &statement.kind {
|
||||
StatementKind::Assign(box (lhs, rhs)) => {
|
||||
self.consume_rvalue(location, rhs);
|
||||
|
||||
self.mutate_place(location, *lhs, Shallow(None));
|
||||
}
|
||||
StatementKind::FakeRead(box (_, _)) => {
|
||||
// Only relevant for initialized/liveness/safety checks.
|
||||
}
|
||||
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => {
|
||||
self.consume_operand(location, op);
|
||||
}
|
||||
StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping {
|
||||
src,
|
||||
dst,
|
||||
count,
|
||||
})) => {
|
||||
self.consume_operand(location, src);
|
||||
self.consume_operand(location, dst);
|
||||
self.consume_operand(location, count);
|
||||
}
|
||||
// Only relevant for mir typeck
|
||||
StatementKind::AscribeUserType(..)
|
||||
// Only relevant for liveness and unsafeck
|
||||
| StatementKind::PlaceMention(..)
|
||||
// Doesn't have any language semantics
|
||||
| StatementKind::Coverage(..)
|
||||
// Does not actually affect borrowck
|
||||
| StatementKind::StorageLive(..) => {}
|
||||
StatementKind::StorageDead(local) => {
|
||||
self.access_place(
|
||||
location,
|
||||
Place::from(*local),
|
||||
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop
|
||||
| StatementKind::Retag { .. }
|
||||
| StatementKind::Deinit(..)
|
||||
| StatementKind::SetDiscriminant { .. } => {
|
||||
bug!("Statement not allowed in this MIR phase")
|
||||
}
|
||||
}
|
||||
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
self.check_activations(location);
|
||||
|
||||
match &terminator.kind {
|
||||
TerminatorKind::SwitchInt { discr, targets: _ } => {
|
||||
self.consume_operand(location, discr);
|
||||
}
|
||||
TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => {
|
||||
let write_kind =
|
||||
if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop };
|
||||
self.access_place(
|
||||
location,
|
||||
*drop_place,
|
||||
(AccessDepth::Drop, Write(write_kind)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
TerminatorKind::Call {
|
||||
func,
|
||||
args,
|
||||
destination,
|
||||
target: _,
|
||||
unwind: _,
|
||||
call_source: _,
|
||||
fn_span: _,
|
||||
} => {
|
||||
self.consume_operand(location, func);
|
||||
for arg in args {
|
||||
self.consume_operand(location, arg);
|
||||
}
|
||||
self.mutate_place(location, *destination, Deep);
|
||||
}
|
||||
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
|
||||
self.consume_operand(location, cond);
|
||||
use rustc_middle::mir::AssertKind;
|
||||
if let AssertKind::BoundsCheck { len, index } = &**msg {
|
||||
self.consume_operand(location, len);
|
||||
self.consume_operand(location, index);
|
||||
}
|
||||
}
|
||||
TerminatorKind::Yield { value, resume, resume_arg, drop: _ } => {
|
||||
self.consume_operand(location, value);
|
||||
|
||||
// Invalidate all borrows of local places
|
||||
let borrow_set = self.borrow_set;
|
||||
let resume = self.location_table.start_index(resume.start_location());
|
||||
for (i, data) in borrow_set.iter_enumerated() {
|
||||
if borrow_of_local_data(data.borrowed_place) {
|
||||
self.all_facts.loan_invalidated_at.push((resume, i));
|
||||
}
|
||||
}
|
||||
|
||||
self.mutate_place(location, *resume_arg, Deep);
|
||||
}
|
||||
TerminatorKind::UnwindResume
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::CoroutineDrop => {
|
||||
// Invalidate all borrows of local places
|
||||
let borrow_set = self.borrow_set;
|
||||
let start = self.location_table.start_index(location);
|
||||
for (i, data) in borrow_set.iter_enumerated() {
|
||||
if borrow_of_local_data(data.borrowed_place) {
|
||||
self.all_facts.loan_invalidated_at.push((start, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
TerminatorKind::InlineAsm {
|
||||
template: _,
|
||||
operands,
|
||||
options: _,
|
||||
line_spans: _,
|
||||
destination: _,
|
||||
unwind: _,
|
||||
} => {
|
||||
for op in operands {
|
||||
match op {
|
||||
InlineAsmOperand::In { reg: _, value } => {
|
||||
self.consume_operand(location, value);
|
||||
}
|
||||
InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
|
||||
if let &Some(place) = place {
|
||||
self.mutate_place(location, place, Shallow(None));
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::InOut { reg: _, late: _, in_value, out_place } => {
|
||||
self.consume_operand(location, in_value);
|
||||
if let &Some(out_place) = out_place {
|
||||
self.mutate_place(location, out_place, Shallow(None));
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::Const { value: _ }
|
||||
| InlineAsmOperand::SymFn { value: _ }
|
||||
| InlineAsmOperand::SymStatic { def_id: _ } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
TerminatorKind::Goto { target: _ }
|
||||
| TerminatorKind::UnwindTerminate(_)
|
||||
| TerminatorKind::Unreachable
|
||||
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
|
||||
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
|
||||
// no data used, thus irrelevant to borrowck
|
||||
}
|
||||
}
|
||||
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
|
||||
/// Simulates mutation of a place.
|
||||
fn mutate_place(&mut self, location: Location, place: Place<'tcx>, kind: AccessDepth) {
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(kind, Write(WriteKind::Mutate)),
|
||||
LocalMutationIsAllowed::ExceptUpvars,
|
||||
);
|
||||
}
|
||||
|
||||
/// Simulates consumption of an operand.
|
||||
fn consume_operand(&mut self, location: Location, operand: &Operand<'tcx>) {
|
||||
match *operand {
|
||||
Operand::Copy(place) => {
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(Deep, Read(ReadKind::Copy)),
|
||||
LocalMutationIsAllowed::No,
|
||||
);
|
||||
}
|
||||
Operand::Move(place) => {
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(Deep, Write(WriteKind::Move)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
);
|
||||
}
|
||||
Operand::Constant(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Simulates consumption of an rvalue
|
||||
fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) {
|
||||
match rvalue {
|
||||
&Rvalue::Ref(_ /*rgn*/, bk, place) => {
|
||||
let access_kind = match bk {
|
||||
BorrowKind::Fake => {
|
||||
(Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
|
||||
}
|
||||
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
|
||||
BorrowKind::Mut { .. } => {
|
||||
let wk = WriteKind::MutableBorrow(bk);
|
||||
if allow_two_phase_borrow(bk) {
|
||||
(Deep, Reservation(wk))
|
||||
} else {
|
||||
(Deep, Write(wk))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
|
||||
}
|
||||
|
||||
&Rvalue::AddressOf(mutability, place) => {
|
||||
let access_kind = match mutability {
|
||||
Mutability::Mut => (
|
||||
Deep,
|
||||
Write(WriteKind::MutableBorrow(BorrowKind::Mut {
|
||||
kind: mir::MutBorrowKind::Default,
|
||||
})),
|
||||
),
|
||||
Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
|
||||
};
|
||||
|
||||
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
|
||||
}
|
||||
|
||||
Rvalue::ThreadLocalRef(_) => {}
|
||||
|
||||
Rvalue::Use(operand)
|
||||
| Rvalue::Repeat(operand, _)
|
||||
| Rvalue::UnaryOp(_ /*un_op*/, operand)
|
||||
| Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/)
|
||||
| Rvalue::ShallowInitBox(operand, _ /*ty*/) => self.consume_operand(location, operand),
|
||||
|
||||
&Rvalue::CopyForDeref(place) => {
|
||||
let op = &Operand::Copy(place);
|
||||
self.consume_operand(location, op);
|
||||
}
|
||||
|
||||
&(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
|
||||
let af = match rvalue {
|
||||
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
|
||||
Rvalue::Discriminant(..) => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(Shallow(af), Read(ReadKind::Copy)),
|
||||
LocalMutationIsAllowed::No,
|
||||
);
|
||||
}
|
||||
|
||||
Rvalue::BinaryOp(_bin_op, box (operand1, operand2))
|
||||
| Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => {
|
||||
self.consume_operand(location, operand1);
|
||||
self.consume_operand(location, operand2);
|
||||
}
|
||||
|
||||
Rvalue::NullaryOp(_op, _ty) => {}
|
||||
|
||||
Rvalue::Aggregate(_, operands) => {
|
||||
for operand in operands {
|
||||
self.consume_operand(location, operand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simulates an access to a place.
|
||||
fn access_place(
|
||||
&mut self,
|
||||
location: Location,
|
||||
place: Place<'tcx>,
|
||||
kind: (AccessDepth, ReadOrWrite),
|
||||
_is_local_mutation_allowed: LocalMutationIsAllowed,
|
||||
) {
|
||||
let (sd, rw) = kind;
|
||||
// note: not doing check_access_permissions checks because they don't generate invalidates
|
||||
self.check_access_for_conflict(location, place, sd, rw);
|
||||
}
|
||||
|
||||
fn check_access_for_conflict(
|
||||
&mut self,
|
||||
location: Location,
|
||||
place: Place<'tcx>,
|
||||
sd: AccessDepth,
|
||||
rw: ReadOrWrite,
|
||||
) {
|
||||
debug!(
|
||||
"check_access_for_conflict(location={:?}, place={:?}, sd={:?}, rw={:?})",
|
||||
location, place, sd, rw,
|
||||
);
|
||||
each_borrow_involving_path(
|
||||
self,
|
||||
self.tcx,
|
||||
self.body,
|
||||
location,
|
||||
(sd, place),
|
||||
self.borrow_set,
|
||||
|_| true,
|
||||
|this, borrow_index, borrow| {
|
||||
match (rw, borrow.kind) {
|
||||
// Obviously an activation is compatible with its own
|
||||
// reservation (or even prior activating uses of same
|
||||
// borrow); so don't check if they interfere.
|
||||
//
|
||||
// NOTE: *reservations* do conflict with themselves;
|
||||
// thus aren't injecting unsoundness w/ this check.)
|
||||
(Activation(_, activating), _) if activating == borrow_index => {
|
||||
// Activating a borrow doesn't generate any invalidations, since we
|
||||
// have already taken the reservation
|
||||
}
|
||||
|
||||
(Read(_), BorrowKind::Fake | BorrowKind::Shared)
|
||||
| (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => {
|
||||
// Reads don't invalidate shared or shallow borrows
|
||||
}
|
||||
|
||||
(Read(_), BorrowKind::Mut { .. }) => {
|
||||
// Reading from mere reservations of mutable-borrows is OK.
|
||||
if !is_active(this.dominators, borrow, location) {
|
||||
// If the borrow isn't active yet, reads don't invalidate it
|
||||
assert!(allow_two_phase_borrow(borrow.kind));
|
||||
return Control::Continue;
|
||||
}
|
||||
|
||||
// Unique and mutable borrows are invalidated by reads from any
|
||||
// involved path
|
||||
this.emit_loan_invalidated_at(borrow_index, location);
|
||||
}
|
||||
|
||||
(Reservation(_) | Activation(_, _) | Write(_), _) => {
|
||||
// unique or mutable borrows are invalidated by writes.
|
||||
// Reservations count as writes since we need to check
|
||||
// that activating the borrow will be OK
|
||||
// FIXME(bob_twinkles) is this actually the right thing to do?
|
||||
this.emit_loan_invalidated_at(borrow_index, location);
|
||||
}
|
||||
}
|
||||
Control::Continue
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Generates a new `loan_invalidated_at(L, B)` fact.
|
||||
fn emit_loan_invalidated_at(&mut self, b: BorrowIndex, l: Location) {
|
||||
let lidx = self.location_table.start_index(l);
|
||||
self.all_facts.loan_invalidated_at.push((lidx, b));
|
||||
}
|
||||
|
||||
fn check_activations(&mut self, location: Location) {
|
||||
// Two-phase borrow support: For each activation that is newly
|
||||
// generated at this statement, check if it interferes with
|
||||
// another borrow.
|
||||
for &borrow_index in self.borrow_set.activations_at_location(location) {
|
||||
let borrow = &self.borrow_set[borrow_index];
|
||||
|
||||
// only mutable borrows should be 2-phase
|
||||
assert!(match borrow.kind {
|
||||
BorrowKind::Shared | BorrowKind::Fake => false,
|
||||
BorrowKind::Mut { .. } => true,
|
||||
});
|
||||
|
||||
self.access_place(
|
||||
location,
|
||||
borrow.borrowed_place,
|
||||
(Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)),
|
||||
LocalMutationIsAllowed::No,
|
||||
);
|
||||
|
||||
// We do not need to call `check_if_path_or_subpath_is_moved`
|
||||
// again, as we already called it when we made the
|
||||
// initial reservation.
|
||||
}
|
||||
}
|
||||
}
|
||||
147
compiler/rustc_borrowck/src/polonius/loan_kills.rs
Normal file
147
compiler/rustc_borrowck/src/polonius/loan_kills.rs
Normal file
@ -0,0 +1,147 @@
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::{
|
||||
Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind,
|
||||
Terminator, TerminatorKind,
|
||||
};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
use crate::{borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, places_conflict};
|
||||
|
||||
/// Emit `loan_killed_at` and `cfg_edge` facts at the same time.
|
||||
pub(super) fn emit_loan_kills<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &mut AllFacts,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, all_facts, body };
|
||||
for (bb, data) in body.basic_blocks.iter_enumerated() {
|
||||
visitor.visit_basic_block_data(bb, data);
|
||||
}
|
||||
}
|
||||
|
||||
struct LoanKillsGenerator<'cx, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &'cx mut AllFacts,
|
||||
location_table: &'cx LocationTable,
|
||||
borrow_set: &'cx BorrowSet<'tcx>,
|
||||
body: &'cx Body<'tcx>,
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'cx, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
// Also record CFG facts here.
|
||||
self.all_facts.cfg_edge.push((
|
||||
self.location_table.start_index(location),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
|
||||
self.all_facts.cfg_edge.push((
|
||||
self.location_table.mid_index(location),
|
||||
self.location_table.start_index(location.successor_within_block()),
|
||||
));
|
||||
|
||||
// If there are borrows on this now dead local, we need to record them as `killed`.
|
||||
if let StatementKind::StorageDead(local) = statement.kind {
|
||||
self.record_killed_borrows_for_local(local, location);
|
||||
}
|
||||
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
||||
fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
// When we see `X = ...`, then kill borrows of
|
||||
// `(*X).foo` and so forth.
|
||||
self.record_killed_borrows_for_place(*place, location);
|
||||
self.super_assign(place, rvalue, location);
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
// Also record CFG facts here.
|
||||
self.all_facts.cfg_edge.push((
|
||||
self.location_table.start_index(location),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
|
||||
let successor_blocks = terminator.successors();
|
||||
self.all_facts.cfg_edge.reserve(successor_blocks.size_hint().0);
|
||||
for successor_block in successor_blocks {
|
||||
self.all_facts.cfg_edge.push((
|
||||
self.location_table.mid_index(location),
|
||||
self.location_table.start_index(successor_block.start_location()),
|
||||
));
|
||||
}
|
||||
|
||||
// A `Call` terminator's return value can be a local which has borrows,
|
||||
// so we need to record those as `killed` as well.
|
||||
if let TerminatorKind::Call { destination, .. } = terminator.kind {
|
||||
self.record_killed_borrows_for_place(destination, location);
|
||||
}
|
||||
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LoanKillsGenerator<'_, 'tcx> {
|
||||
/// Records the borrows on the specified place as `killed`. For example, when assigning to a
|
||||
/// local, or on a call's return destination.
|
||||
fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) {
|
||||
// Depending on the `Place` we're killing:
|
||||
// - if it's a local, or a single deref of a local,
|
||||
// we kill all the borrows on the local.
|
||||
// - if it's a deeper projection, we have to filter which
|
||||
// of the borrows are killed: the ones whose `borrowed_place`
|
||||
// conflicts with the `place`.
|
||||
match place.as_ref() {
|
||||
PlaceRef { local, projection: &[] }
|
||||
| PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
|
||||
debug!(
|
||||
"Recording `killed` facts for borrows of local={:?} at location={:?}",
|
||||
local, location
|
||||
);
|
||||
|
||||
self.record_killed_borrows_for_local(local, location);
|
||||
}
|
||||
|
||||
PlaceRef { local, projection: &[.., _] } => {
|
||||
// Kill conflicting borrows of the innermost local.
|
||||
debug!(
|
||||
"Recording `killed` facts for borrows of \
|
||||
innermost projected local={:?} at location={:?}",
|
||||
local, location
|
||||
);
|
||||
|
||||
if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
|
||||
for &borrow_index in borrow_indices {
|
||||
let places_conflict = places_conflict::places_conflict(
|
||||
self.tcx,
|
||||
self.body,
|
||||
self.borrow_set[borrow_index].borrowed_place,
|
||||
place,
|
||||
places_conflict::PlaceConflictBias::NoOverlap,
|
||||
);
|
||||
|
||||
if places_conflict {
|
||||
let location_index = self.location_table.mid_index(location);
|
||||
self.all_facts.loan_killed_at.push((borrow_index, location_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Records the borrows on the specified local as `killed`.
|
||||
fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) {
|
||||
if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
|
||||
let location_index = self.location_table.mid_index(location);
|
||||
self.all_facts.loan_killed_at.reserve(borrow_indices.len());
|
||||
for &borrow_index in borrow_indices {
|
||||
self.all_facts.loan_killed_at.push((borrow_index, location_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
188
compiler/rustc_borrowck/src/polonius/mod.rs
Normal file
188
compiler/rustc_borrowck/src/polonius/mod.rs
Normal file
@ -0,0 +1,188 @@
|
||||
//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation.
|
||||
//!
|
||||
//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature
|
||||
//! parity.
|
||||
|
||||
use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
|
||||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::facts::AllFacts;
|
||||
use crate::location::LocationTable;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
mod loan_invalidations;
|
||||
mod loan_kills;
|
||||
|
||||
/// When requested, emit most of the facts needed by polonius:
|
||||
/// - moves and assignments
|
||||
/// - universal regions and their relations
|
||||
/// - CFG points and edges
|
||||
/// - loan kills
|
||||
/// - loan invalidations
|
||||
///
|
||||
/// The rest of the facts are emitted during typeck and liveness.
|
||||
pub(crate) fn emit_facts<'tcx>(
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
move_data: &MoveData<'_>,
|
||||
universal_regions: &UniversalRegions<'_>,
|
||||
universal_region_relations: &UniversalRegionRelations<'_>,
|
||||
) {
|
||||
let Some(all_facts) = all_facts else {
|
||||
// We don't do anything if there are no facts to fill.
|
||||
return;
|
||||
};
|
||||
let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
|
||||
emit_move_facts(all_facts, move_data, location_table, body);
|
||||
emit_universal_region_facts(
|
||||
all_facts,
|
||||
borrow_set,
|
||||
&universal_regions,
|
||||
&universal_region_relations,
|
||||
);
|
||||
emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set);
|
||||
emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set);
|
||||
}
|
||||
|
||||
/// Emit facts needed for move/init analysis: moves and assignments.
|
||||
fn emit_move_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
move_data: &MoveData<'_>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'_>,
|
||||
) {
|
||||
all_facts
|
||||
.path_is_var
|
||||
.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
|
||||
|
||||
for (child, move_path) in move_data.move_paths.iter_enumerated() {
|
||||
if let Some(parent) = move_path.parent {
|
||||
all_facts.child_path.push((child, parent));
|
||||
}
|
||||
}
|
||||
|
||||
let fn_entry_start =
|
||||
location_table.start_index(Location { block: START_BLOCK, statement_index: 0 });
|
||||
|
||||
// initialized_at
|
||||
for init in move_data.inits.iter() {
|
||||
match init.location {
|
||||
InitLocation::Statement(location) => {
|
||||
let block_data = &body[location.block];
|
||||
let is_terminator = location.statement_index == block_data.statements.len();
|
||||
|
||||
if is_terminator && init.kind == InitKind::NonPanicPathOnly {
|
||||
// We are at the terminator of an init that has a panic path,
|
||||
// and where the init should not happen on panic
|
||||
|
||||
for successor in block_data.terminator().successors() {
|
||||
if body[successor].is_cleanup {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The initialization happened in (or rather, when arriving at)
|
||||
// the successors, but not in the unwind block.
|
||||
let first_statement = Location { block: successor, statement_index: 0 };
|
||||
all_facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.start_index(first_statement)));
|
||||
}
|
||||
} else {
|
||||
// In all other cases, the initialization just happens at the
|
||||
// midpoint, like any other effect.
|
||||
all_facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.mid_index(location)));
|
||||
}
|
||||
}
|
||||
// Arguments are initialized on function entry
|
||||
InitLocation::Argument(local) => {
|
||||
assert!(body.local_kind(local) == LocalKind::Arg);
|
||||
all_facts.path_assigned_at_base.push((init.path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (local, path) in move_data.rev_lookup.iter_locals_enumerated() {
|
||||
if body.local_kind(local) != LocalKind::Arg {
|
||||
// Non-arguments start out deinitialised; we simulate this with an
|
||||
// initial move:
|
||||
all_facts.path_moved_at_base.push((path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
|
||||
// moved_out_at
|
||||
// deinitialisation is assumed to always happen!
|
||||
all_facts
|
||||
.path_moved_at_base
|
||||
.extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))));
|
||||
}
|
||||
|
||||
/// Emit universal regions facts, and their relations.
|
||||
fn emit_universal_region_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
borrow_set: &BorrowSet<'_>,
|
||||
universal_regions: &UniversalRegions<'_>,
|
||||
universal_region_relations: &UniversalRegionRelations<'_>,
|
||||
) {
|
||||
// 1: universal regions are modeled in Polonius as a pair:
|
||||
// - the universal region vid itself.
|
||||
// - a "placeholder loan" associated to this universal region. Since they don't exist in
|
||||
// the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
|
||||
// added to the existing number of loans, as if they succeeded them in the set.
|
||||
//
|
||||
all_facts.universal_region.extend(universal_regions.universal_regions());
|
||||
let borrow_count = borrow_set.len();
|
||||
debug!(
|
||||
"emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}",
|
||||
universal_regions.len(),
|
||||
borrow_count
|
||||
);
|
||||
|
||||
for universal_region in universal_regions.universal_regions() {
|
||||
let universal_region_idx = universal_region.index();
|
||||
let placeholder_loan_idx = borrow_count + universal_region_idx;
|
||||
all_facts.placeholder.push((universal_region, placeholder_loan_idx.into()));
|
||||
}
|
||||
|
||||
// 2: the universal region relations `outlives` constraints are emitted as
|
||||
// `known_placeholder_subset` facts.
|
||||
for (fr1, fr2) in universal_region_relations.known_outlives() {
|
||||
if fr1 != fr2 {
|
||||
debug!(
|
||||
"emit_universal_region_facts: emitting polonius `known_placeholder_subset` \
|
||||
fr1={:?}, fr2={:?}",
|
||||
fr1, fr2
|
||||
);
|
||||
all_facts.known_placeholder_subset.push((fr1, fr2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit facts about loan invalidations.
|
||||
fn emit_loan_invalidations_facts<'tcx>(
|
||||
all_facts: &mut AllFacts,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set);
|
||||
}
|
||||
|
||||
/// Emit facts about CFG points and edges, as well as locations where loans are killed.
|
||||
fn emit_cfg_and_loan_kills_facts<'tcx>(
|
||||
all_facts: &mut AllFacts,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set);
|
||||
}
|
||||
@ -67,7 +67,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
with_msg: &mut dyn FnMut(&str) -> io::Result<()>,
|
||||
) -> io::Result<()> {
|
||||
for region in self.definitions.indices() {
|
||||
let value = self.liveness_constraints.region_value_str(region);
|
||||
let value = self.liveness_constraints.pretty_print_live_points(region);
|
||||
if value != "{}" {
|
||||
with_msg(&format!("{region:?} live at {value}"))?;
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ use std::borrow::Cow;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use super::*;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use rustc_graphviz as dot;
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
@ -7,7 +7,6 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::scc::Sccs;
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
use rustc_index::bit_set::SparseBitMatrix;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_infer::infer::outlives::test_type_match;
|
||||
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
|
||||
@ -31,8 +30,8 @@ use crate::{
|
||||
nll::PoloniusOutput,
|
||||
region_infer::reverse_sccs::ReverseSccGraph,
|
||||
region_infer::values::{
|
||||
LivenessValues, PlaceholderIndices, PointIndex, RegionElement, RegionValueElements,
|
||||
RegionValues, ToElementIndex,
|
||||
LivenessValues, PlaceholderIndices, RegionElement, RegionValueElements, RegionValues,
|
||||
ToElementIndex,
|
||||
},
|
||||
type_check::{free_region_relations::UniversalRegionRelations, Locations},
|
||||
universal_regions::UniversalRegions,
|
||||
@ -59,7 +58,7 @@ pub struct RegionInferenceContext<'tcx> {
|
||||
/// regions, these start out empty and steadily grow, though for
|
||||
/// each universally quantified region R they start out containing
|
||||
/// the entire CFG and `end(R)`.
|
||||
liveness_constraints: LivenessValues<RegionVid>,
|
||||
liveness_constraints: LivenessValues,
|
||||
|
||||
/// The outlives constraints computed by the type-check.
|
||||
constraints: Frozen<OutlivesConstraintSet<'tcx>>,
|
||||
@ -120,9 +119,6 @@ pub struct RegionInferenceContext<'tcx> {
|
||||
/// Information about how the universally quantified regions in
|
||||
/// scope on this function relate to one another.
|
||||
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
|
||||
|
||||
/// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`.
|
||||
live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
|
||||
}
|
||||
|
||||
/// Each time that `apply_member_constraint` is successful, it appends
|
||||
@ -151,6 +147,7 @@ pub(crate) struct AppliedMemberConstraint {
|
||||
pub(crate) member_constraint_index: NllMemberConstraintIndex,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RegionDefinition<'tcx> {
|
||||
/// What kind of variable is this -- a free region? existential
|
||||
/// variable? etc. (See the `NllRegionVariableOrigin` for more
|
||||
@ -332,9 +329,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
|
||||
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
|
||||
type_tests: Vec<TypeTest<'tcx>>,
|
||||
liveness_constraints: LivenessValues<RegionVid>,
|
||||
liveness_constraints: LivenessValues,
|
||||
elements: &Rc<RegionValueElements>,
|
||||
live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
|
||||
) -> Self {
|
||||
debug!("universal_regions: {:#?}", universal_regions);
|
||||
debug!("outlives constraints: {:#?}", outlives_constraints);
|
||||
@ -359,7 +355,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
let mut scc_values =
|
||||
RegionValues::new(elements, universal_regions.len(), &placeholder_indices);
|
||||
|
||||
for region in liveness_constraints.rows() {
|
||||
for region in liveness_constraints.regions() {
|
||||
let scc = constraint_sccs.scc(region);
|
||||
scc_values.merge_liveness(scc, region, &liveness_constraints);
|
||||
}
|
||||
@ -388,7 +384,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
type_tests,
|
||||
universal_regions,
|
||||
universal_region_relations,
|
||||
live_loans,
|
||||
};
|
||||
|
||||
result.init_free_and_bound_regions();
|
||||
@ -679,13 +674,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// eagerly.
|
||||
let mut outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new);
|
||||
|
||||
self.check_type_tests(
|
||||
infcx,
|
||||
param_env,
|
||||
body,
|
||||
outlives_requirements.as_mut(),
|
||||
&mut errors_buffer,
|
||||
);
|
||||
self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer);
|
||||
|
||||
debug!(?errors_buffer);
|
||||
debug!(?outlives_requirements);
|
||||
|
||||
// In Polonius mode, the errors about missing universal region relations are in the output
|
||||
// and need to be emitted or propagated. Otherwise, we need to check whether the
|
||||
@ -700,10 +692,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
self.check_universal_regions(outlives_requirements.as_mut(), &mut errors_buffer);
|
||||
}
|
||||
|
||||
debug!(?errors_buffer);
|
||||
|
||||
if errors_buffer.is_empty() {
|
||||
self.check_member_constraints(infcx, &mut errors_buffer);
|
||||
}
|
||||
|
||||
debug!(?errors_buffer);
|
||||
|
||||
let outlives_requirements = outlives_requirements.unwrap_or_default();
|
||||
|
||||
if outlives_requirements.is_empty() {
|
||||
@ -936,7 +932,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fn check_type_tests(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
|
||||
errors_buffer: &mut RegionErrors<'tcx>,
|
||||
@ -954,7 +949,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
let generic_ty = type_test.generic_kind.to_ty(tcx);
|
||||
if self.eval_verify_bound(
|
||||
infcx,
|
||||
param_env,
|
||||
generic_ty,
|
||||
type_test.lower_bound,
|
||||
&type_test.verify_bound,
|
||||
@ -965,7 +959,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements {
|
||||
if self.try_promote_type_test(
|
||||
infcx,
|
||||
param_env,
|
||||
body,
|
||||
type_test,
|
||||
propagated_outlives_requirements,
|
||||
@ -1023,7 +1016,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fn try_promote_type_test(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
type_test: &TypeTest<'tcx>,
|
||||
propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'tcx>>,
|
||||
@ -1085,7 +1077,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// where `ur` is a local bound -- we are sometimes in a
|
||||
// position to prove things that our caller cannot. See
|
||||
// #53570 for an example.
|
||||
if self.eval_verify_bound(infcx, param_env, generic_ty, ur, &type_test.verify_bound) {
|
||||
if self.eval_verify_bound(infcx, generic_ty, ur, &type_test.verify_bound) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1268,7 +1260,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fn eval_verify_bound(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
generic_ty: Ty<'tcx>,
|
||||
lower_bound: RegionVid,
|
||||
verify_bound: &VerifyBound<'tcx>,
|
||||
@ -1277,7 +1268,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
match verify_bound {
|
||||
VerifyBound::IfEq(verify_if_eq_b) => {
|
||||
self.eval_if_eq(infcx, param_env, generic_ty, lower_bound, *verify_if_eq_b)
|
||||
self.eval_if_eq(infcx, generic_ty, lower_bound, *verify_if_eq_b)
|
||||
}
|
||||
|
||||
VerifyBound::IsEmpty => {
|
||||
@ -1291,11 +1282,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
|
||||
VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| {
|
||||
self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound)
|
||||
self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound)
|
||||
}),
|
||||
|
||||
VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| {
|
||||
self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound)
|
||||
self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound)
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -1303,19 +1294,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fn eval_if_eq(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
generic_ty: Ty<'tcx>,
|
||||
lower_bound: RegionVid,
|
||||
verify_if_eq_b: ty::Binder<'tcx, VerifyIfEq<'tcx>>,
|
||||
) -> bool {
|
||||
let generic_ty = self.normalize_to_scc_representatives(infcx.tcx, generic_ty);
|
||||
let verify_if_eq_b = self.normalize_to_scc_representatives(infcx.tcx, verify_if_eq_b);
|
||||
match test_type_match::extract_verify_if_eq(
|
||||
infcx.tcx,
|
||||
param_env,
|
||||
&verify_if_eq_b,
|
||||
generic_ty,
|
||||
) {
|
||||
match test_type_match::extract_verify_if_eq(infcx.tcx, &verify_if_eq_b, generic_ty) {
|
||||
Some(r) => {
|
||||
let r_vid = self.to_region_vid(r);
|
||||
self.eval_outlives(r_vid, lower_bound)
|
||||
@ -1457,6 +1442,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
errors_buffer: &mut RegionErrors<'tcx>,
|
||||
) {
|
||||
for (fr, fr_definition) in self.definitions.iter_enumerated() {
|
||||
debug!(?fr, ?fr_definition);
|
||||
match fr_definition.origin {
|
||||
NllRegionVariableOrigin::FreeRegion => {
|
||||
// Go through each of the universal regions `fr` and check that
|
||||
@ -1963,15 +1949,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Finds some region R such that `fr1: R` and `R` is live at `elem`.
|
||||
/// Finds some region R such that `fr1: R` and `R` is live at `location`.
|
||||
#[instrument(skip(self), level = "trace", ret)]
|
||||
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
|
||||
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
|
||||
trace!(scc = ?self.constraint_sccs.scc(fr1));
|
||||
trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]);
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `elem`
|
||||
trace!(?r, liveness_constraints=?self.liveness_constraints.region_value_str(r));
|
||||
self.liveness_constraints.contains(r, elem)
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `location`
|
||||
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
|
||||
self.liveness_constraints.is_live_at(r, location)
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find that, we may find some `r` such that
|
||||
@ -2316,7 +2302,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`.
|
||||
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool {
|
||||
let point = self.liveness_constraints.point_from_location(location);
|
||||
self.live_loans.contains(point, loan_idx)
|
||||
self.liveness_constraints.is_loan_live_at(loan_idx, point)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// call `infer_opaque_definition_from_instantiation` to get the inferred
|
||||
/// type of `_Return<'_a>`. `infer_opaque_definition_from_instantiation`
|
||||
/// compares lifetimes directly, so we need to map the inference variables
|
||||
/// back to concrete lifetimes: `'static`, `ReEarlyBound` or `ReFree`.
|
||||
/// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
|
||||
///
|
||||
/// First we map all the lifetimes in the concrete type to an equal
|
||||
/// universal region that occurs in the concrete type's args, in this case
|
||||
@ -315,7 +315,7 @@ fn check_opaque_type_well_formed<'tcx>(
|
||||
parent_def_id = tcx.local_parent(parent_def_id);
|
||||
}
|
||||
|
||||
// FIXME(-Ztrait-solver=next): We probably should use `DefiningAnchor::Error`
|
||||
// FIXME(-Znext-solver): We probably should use `DefiningAnchor::Error`
|
||||
// and prepopulate this `InferCtxt` with known opaque values, rather than
|
||||
// using the `Bind` anchor here. For now it's fine.
|
||||
let infcx = tcx
|
||||
@ -386,7 +386,7 @@ fn check_opaque_type_parameter_valid(
|
||||
let arg_is_param = match arg.unpack() {
|
||||
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
|
||||
GenericArgKind::Lifetime(lt) if is_ty_alias => {
|
||||
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
|
||||
matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_))
|
||||
}
|
||||
// FIXME(#113916): we can't currently check for unique lifetime params,
|
||||
// see that issue for more. We will also have to ignore unused lifetime
|
||||
|
||||
@ -11,6 +11,8 @@ use rustc_middle::ty::{self, RegionVid};
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::dataflow::BorrowIndex;
|
||||
|
||||
/// Maps between a `Location` and a `PointIndex` (and vice versa).
|
||||
pub(crate) struct RegionValueElements {
|
||||
/// For each basic block, how many points are contained within?
|
||||
@ -90,6 +92,7 @@ impl RegionValueElements {
|
||||
rustc_index::newtype_index! {
|
||||
/// A single integer representing a `Location` in the MIR control-flow
|
||||
/// graph. Constructed efficiently from `RegionValueElements`.
|
||||
#[orderable]
|
||||
#[debug_format = "PointIndex({})"]
|
||||
pub struct PointIndex {}
|
||||
}
|
||||
@ -116,71 +119,132 @@ pub(crate) enum RegionElement {
|
||||
PlaceholderRegion(ty::PlaceholderRegion),
|
||||
}
|
||||
|
||||
/// When we initially compute liveness, we use an interval matrix storing
|
||||
/// liveness ranges for each region-vid.
|
||||
pub(crate) struct LivenessValues<N: Idx> {
|
||||
/// Records the CFG locations where each region is live. When we initially compute liveness, we use
|
||||
/// an interval matrix storing liveness ranges for each region-vid.
|
||||
pub(crate) struct LivenessValues {
|
||||
/// The map from locations to points.
|
||||
elements: Rc<RegionValueElements>,
|
||||
points: SparseIntervalMatrix<N, PointIndex>,
|
||||
|
||||
/// For each region: the points where it is live.
|
||||
points: SparseIntervalMatrix<RegionVid, PointIndex>,
|
||||
|
||||
/// When using `-Zpolonius=next`, for each point: the loans flowing into the live regions at
|
||||
/// that point.
|
||||
pub(crate) loans: Option<LiveLoans>,
|
||||
}
|
||||
|
||||
impl<N: Idx> LivenessValues<N> {
|
||||
/// Creates a new set of "region values" that tracks causal information.
|
||||
/// Each of the regions in num_region_variables will be initialized with an
|
||||
/// empty set of points and no causal information.
|
||||
/// Data used to compute the loans that are live at a given point in the CFG, when using
|
||||
/// `-Zpolonius=next`.
|
||||
pub(crate) struct LiveLoans {
|
||||
/// The set of loans that flow into a given region. When individual regions are marked as live
|
||||
/// in the CFG, these inflowing loans are recorded as live.
|
||||
pub(crate) inflowing_loans: SparseBitMatrix<RegionVid, BorrowIndex>,
|
||||
|
||||
/// The set of loans that are live at a given point in the CFG.
|
||||
pub(crate) live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
|
||||
}
|
||||
|
||||
impl LiveLoans {
|
||||
pub(crate) fn new(num_loans: usize) -> Self {
|
||||
LiveLoans {
|
||||
live_loans: SparseBitMatrix::new(num_loans),
|
||||
inflowing_loans: SparseBitMatrix::new(num_loans),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LivenessValues {
|
||||
/// Create an empty map of regions to locations where they're live.
|
||||
pub(crate) fn new(elements: Rc<RegionValueElements>) -> Self {
|
||||
Self { points: SparseIntervalMatrix::new(elements.num_points), elements }
|
||||
LivenessValues {
|
||||
points: SparseIntervalMatrix::new(elements.num_points),
|
||||
elements,
|
||||
loans: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate through each region that has a value in this set.
|
||||
pub(crate) fn rows(&self) -> impl Iterator<Item = N> {
|
||||
pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> {
|
||||
self.points.rows()
|
||||
}
|
||||
|
||||
/// Adds the given element to the value for the given region. Returns whether
|
||||
/// the element is newly added (i.e., was not already present).
|
||||
pub(crate) fn add_element(&mut self, row: N, location: Location) -> bool {
|
||||
debug!("LivenessValues::add(r={:?}, location={:?})", row, location);
|
||||
let index = self.elements.point_from_location(location);
|
||||
self.points.insert(row, index)
|
||||
/// Records `region` as being live at the given `location`.
|
||||
pub(crate) fn add_location(&mut self, region: RegionVid, location: Location) {
|
||||
debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location);
|
||||
let point = self.elements.point_from_location(location);
|
||||
self.points.insert(region, point);
|
||||
|
||||
// When available, record the loans flowing into this region as live at the given point.
|
||||
if let Some(loans) = self.loans.as_mut() {
|
||||
if let Some(inflowing) = loans.inflowing_loans.row(region) {
|
||||
loans.live_loans.union_row(point, inflowing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds all the elements in the given bit array into the given
|
||||
/// region. Returns whether any of them are newly added.
|
||||
pub(crate) fn add_elements(&mut self, row: N, locations: &IntervalSet<PointIndex>) -> bool {
|
||||
debug!("LivenessValues::add_elements(row={:?}, locations={:?})", row, locations);
|
||||
self.points.union_row(row, locations)
|
||||
/// Records `region` as being live at all the given `points`.
|
||||
pub(crate) fn add_points(&mut self, region: RegionVid, points: &IntervalSet<PointIndex>) {
|
||||
debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points);
|
||||
self.points.union_row(region, points);
|
||||
|
||||
// When available, record the loans flowing into this region as live at the given points.
|
||||
if let Some(loans) = self.loans.as_mut() {
|
||||
if let Some(inflowing) = loans.inflowing_loans.row(region) {
|
||||
if !inflowing.is_empty() {
|
||||
for point in points.iter() {
|
||||
loans.live_loans.union_row(point, inflowing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds all the control-flow points to the values for `r`.
|
||||
pub(crate) fn add_all_points(&mut self, row: N) {
|
||||
self.points.insert_all_into_row(row);
|
||||
/// Records `region` as being live at all the control-flow points.
|
||||
pub(crate) fn add_all_points(&mut self, region: RegionVid) {
|
||||
self.points.insert_all_into_row(region);
|
||||
}
|
||||
|
||||
/// Returns `true` if the region `r` contains the given element.
|
||||
pub(crate) fn contains(&self, row: N, location: Location) -> bool {
|
||||
let index = self.elements.point_from_location(location);
|
||||
self.points.row(row).is_some_and(|r| r.contains(index))
|
||||
/// Returns whether `region` is marked live at the given `location`.
|
||||
pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool {
|
||||
let point = self.elements.point_from_location(location);
|
||||
self.points.row(region).is_some_and(|r| r.contains(point))
|
||||
}
|
||||
|
||||
/// Returns an iterator of all the elements contained by the region `r`
|
||||
pub(crate) fn get_elements(&self, row: N) -> impl Iterator<Item = Location> + '_ {
|
||||
/// Returns whether `region` is marked live at any location.
|
||||
pub(crate) fn is_live_anywhere(&self, region: RegionVid) -> bool {
|
||||
self.live_points(region).next().is_some()
|
||||
}
|
||||
|
||||
/// Returns an iterator of all the points where `region` is live.
|
||||
fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> + '_ {
|
||||
self.points
|
||||
.row(row)
|
||||
.row(region)
|
||||
.into_iter()
|
||||
.flat_map(|set| set.iter())
|
||||
.take_while(move |&p| self.elements.point_in_range(p))
|
||||
.map(move |p| self.elements.to_location(p))
|
||||
.take_while(|&p| self.elements.point_in_range(p))
|
||||
}
|
||||
|
||||
/// Returns a "pretty" string value of the region. Meant for debugging.
|
||||
pub(crate) fn region_value_str(&self, r: N) -> String {
|
||||
region_value_str(self.get_elements(r).map(RegionElement::Location))
|
||||
/// For debugging purposes, returns a pretty-printed string of the points where the `region` is
|
||||
/// live.
|
||||
pub(crate) fn pretty_print_live_points(&self, region: RegionVid) -> String {
|
||||
pretty_print_region_elements(
|
||||
self.live_points(region).map(|p| RegionElement::Location(self.elements.to_location(p))),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn point_from_location(&self, location: Location) -> PointIndex {
|
||||
self.elements.point_from_location(location)
|
||||
}
|
||||
|
||||
/// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`.
|
||||
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool {
|
||||
self.loans
|
||||
.as_ref()
|
||||
.expect("Accessing live loans requires `-Zpolonius=next`")
|
||||
.live_loans
|
||||
.contains(point, loan_idx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps from `ty::PlaceholderRegion` values that are used in the rest of
|
||||
@ -307,7 +371,7 @@ impl<N: Idx> RegionValues<N> {
|
||||
/// `self[to] |= values[from]`, essentially: that is, take all the
|
||||
/// elements for the region `from` from `values` and add them to
|
||||
/// the region `to` in `self`.
|
||||
pub(crate) fn merge_liveness<M: Idx>(&mut self, to: N, from: M, values: &LivenessValues<M>) {
|
||||
pub(crate) fn merge_liveness(&mut self, to: N, from: RegionVid, values: &LivenessValues) {
|
||||
if let Some(set) = values.points.row(from) {
|
||||
self.points.union_row(to, set);
|
||||
}
|
||||
@ -376,7 +440,7 @@ impl<N: Idx> RegionValues<N> {
|
||||
|
||||
/// Returns a "pretty" string value of the region. Meant for debugging.
|
||||
pub(crate) fn region_value_str(&self, r: N) -> String {
|
||||
region_value_str(self.elements_contained_in(r))
|
||||
pretty_print_region_elements(self.elements_contained_in(r))
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,11 +484,12 @@ impl ToElementIndex for ty::PlaceholderRegion {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn location_set_str(
|
||||
/// For debugging purposes, returns a pretty-printed string of the given points.
|
||||
pub(crate) fn pretty_print_points(
|
||||
elements: &RegionValueElements,
|
||||
points: impl IntoIterator<Item = PointIndex>,
|
||||
) -> String {
|
||||
region_value_str(
|
||||
pretty_print_region_elements(
|
||||
points
|
||||
.into_iter()
|
||||
.take_while(|&p| elements.point_in_range(p))
|
||||
@ -433,7 +498,8 @@ pub(crate) fn location_set_str(
|
||||
)
|
||||
}
|
||||
|
||||
fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String {
|
||||
/// For debugging purposes, returns a pretty-printed string of the given region elements.
|
||||
fn pretty_print_region_elements(elements: impl IntoIterator<Item = RegionElement>) -> String {
|
||||
let mut result = String::new();
|
||||
result.push('{');
|
||||
|
||||
|
||||
@ -28,6 +28,9 @@ pub fn renumber_mir<'tcx>(
|
||||
renumberer.visit_body(body);
|
||||
}
|
||||
|
||||
// FIXME(@lcnr): A lot of these variants overlap and it seems like
|
||||
// this type is only used to decide which region should be used
|
||||
// as representative. This should be cleaned up.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub(crate) enum RegionCtxt {
|
||||
Location(Location),
|
||||
|
||||
@ -7,7 +7,7 @@ use rustc_infer::infer::region_constraints::GenericKind;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::traits::query::OutlivesBound;
|
||||
use rustc_middle::ty::{self, RegionVid, Ty};
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt};
|
||||
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
|
||||
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
|
||||
use std::rc::Rc;
|
||||
@ -303,7 +303,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
|
||||
Locations::All(span),
|
||||
span,
|
||||
ConstraintCategory::Internal,
|
||||
&mut self.constraints,
|
||||
self.constraints,
|
||||
)
|
||||
.convert_all(data);
|
||||
}
|
||||
@ -321,6 +321,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
|
||||
.map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty))
|
||||
.ok()?;
|
||||
debug!(?bounds, ?constraints);
|
||||
// Because of #109628, we may have unexpected placeholders. Ignore them!
|
||||
// FIXME(#109628): panic in this case once the issue is fixed.
|
||||
let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders());
|
||||
self.add_outlives_bounds(bounds);
|
||||
constraints
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//! `RETURN_PLACE` the MIR arguments) are always fully normalized (and
|
||||
//! contain revealed `impl Trait` values).
|
||||
|
||||
use rustc_infer::infer::LateBoundRegionConversionTime;
|
||||
use rustc_infer::infer::BoundRegionConversionTime;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
@ -35,7 +35,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
.instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig);
|
||||
let user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars(
|
||||
body.span,
|
||||
LateBoundRegionConversionTime::FnCall,
|
||||
BoundRegionConversionTime::FnCall,
|
||||
user_provided_sig,
|
||||
);
|
||||
|
||||
@ -77,7 +77,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
if argument_index + 1 >= body.local_decls.len() {
|
||||
self.tcx()
|
||||
.sess
|
||||
.delay_span_bug(body.span, "found more normalized_input_ty than local_decls");
|
||||
.span_delayed_bug(body.span, "found more normalized_input_ty than local_decls");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -101,10 +101,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
);
|
||||
|
||||
// We will not have a universal_regions.yield_ty if we yield (by accident)
|
||||
// outside of a coroutine and return an `impl Trait`, so emit a delay_span_bug
|
||||
// outside of a coroutine and return an `impl Trait`, so emit a span_delayed_bug
|
||||
// because we don't want to panic in an assert here if we've already got errors.
|
||||
if body.yield_ty().is_some() != universal_regions.yield_ty.is_some() {
|
||||
self.tcx().sess.delay_span_bug(
|
||||
self.tcx().sess.span_delayed_bug(
|
||||
body.span,
|
||||
format!(
|
||||
"Expected body to have yield_ty ({:?}) iff we have a UR yield_ty ({:?})",
|
||||
|
||||
@ -80,7 +80,7 @@ impl LocalUseMap {
|
||||
live_locals.iter().for_each(|&local| locals_with_use_data[local] = true);
|
||||
|
||||
LocalUseMapBuild { local_use_map: &mut local_use_map, elements, locals_with_use_data }
|
||||
.visit_body(&body);
|
||||
.visit_body(body);
|
||||
|
||||
local_use_map
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
use itertools::{Either, Itertools};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::mir::{Body, Local};
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_middle::mir::visit::{TyContext, Visitor};
|
||||
use rustc_middle::mir::{Body, Local, Location, SourceInfo};
|
||||
use rustc_middle::ty::visit::TypeVisitable;
|
||||
use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt};
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
@ -11,7 +13,7 @@ use crate::{
|
||||
constraints::OutlivesConstraintSet,
|
||||
facts::{AllFacts, AllFactsExt},
|
||||
location::LocationTable,
|
||||
region_infer::values::RegionValueElements,
|
||||
region_infer::values::{LivenessValues, RegionValueElements},
|
||||
universal_regions::UniversalRegions,
|
||||
};
|
||||
|
||||
@ -42,11 +44,11 @@ pub(super) fn generate<'mir, 'tcx>(
|
||||
|
||||
let free_regions = regions_that_outlive_free_regions(
|
||||
typeck.infcx.num_region_vars(),
|
||||
&typeck.borrowck_context.universal_regions,
|
||||
typeck.borrowck_context.universal_regions,
|
||||
&typeck.borrowck_context.constraints.outlives_constraints,
|
||||
);
|
||||
let (relevant_live_locals, boring_locals) =
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, &body);
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, body);
|
||||
let facts_enabled = use_polonius || AllFacts::enabled(typeck.tcx());
|
||||
|
||||
let polonius_drop_used = facts_enabled.then(|| {
|
||||
@ -65,6 +67,14 @@ pub(super) fn generate<'mir, 'tcx>(
|
||||
boring_locals,
|
||||
polonius_drop_used,
|
||||
);
|
||||
|
||||
// Mark regions that should be live where they appear within rvalues or within a call: like
|
||||
// args, regions, and types.
|
||||
record_regular_live_regions(
|
||||
typeck.tcx(),
|
||||
&mut typeck.borrowck_context.constraints.liveness_constraints,
|
||||
body,
|
||||
);
|
||||
}
|
||||
|
||||
// The purpose of `compute_relevant_live_locals` is to define the subset of `Local`
|
||||
@ -132,3 +142,71 @@ fn regions_that_outlive_free_regions<'tcx>(
|
||||
// Return the final set of things we visited.
|
||||
outlives_free_region
|
||||
}
|
||||
|
||||
/// Some variables are "regular live" at `location` -- i.e., they may be used later. This means that
|
||||
/// all regions appearing in their type must be live at `location`.
|
||||
fn record_regular_live_regions<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
liveness_constraints: &mut LivenessValues,
|
||||
body: &Body<'tcx>,
|
||||
) {
|
||||
let mut visitor = LiveVariablesVisitor { tcx, liveness_constraints };
|
||||
for (bb, data) in body.basic_blocks.iter_enumerated() {
|
||||
visitor.visit_basic_block_data(bb, data);
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor looking for regions that should be live within rvalues or calls.
|
||||
struct LiveVariablesVisitor<'cx, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
liveness_constraints: &'cx mut LivenessValues,
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'cx, 'tcx> {
|
||||
/// We sometimes have `args` within an rvalue, or within a
|
||||
/// call. Make them live at the location where they appear.
|
||||
fn visit_args(&mut self, args: &GenericArgsRef<'tcx>, location: Location) {
|
||||
self.record_regions_live_at(*args, location);
|
||||
self.super_args(args);
|
||||
}
|
||||
|
||||
/// We sometimes have `region`s within an rvalue, or within a
|
||||
/// call. Make them live at the location where they appear.
|
||||
fn visit_region(&mut self, region: Region<'tcx>, location: Location) {
|
||||
self.record_regions_live_at(region, location);
|
||||
self.super_region(region);
|
||||
}
|
||||
|
||||
/// We sometimes have `ty`s within an rvalue, or within a
|
||||
/// call. Make them live at the location where they appear.
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>, ty_context: TyContext) {
|
||||
match ty_context {
|
||||
TyContext::ReturnTy(SourceInfo { span, .. })
|
||||
| TyContext::YieldTy(SourceInfo { span, .. })
|
||||
| TyContext::UserTy(span)
|
||||
| TyContext::LocalDecl { source_info: SourceInfo { span, .. }, .. } => {
|
||||
span_bug!(span, "should not be visiting outside of the CFG: {:?}", ty_context);
|
||||
}
|
||||
TyContext::Location(location) => {
|
||||
self.record_regions_live_at(ty, location);
|
||||
}
|
||||
}
|
||||
|
||||
self.super_ty(ty);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> LiveVariablesVisitor<'cx, 'tcx> {
|
||||
/// Some variable is "regular live" at `location` -- i.e., it may be used later. This means that
|
||||
/// all regions appearing in the type of `value` must be live at `location`.
|
||||
fn record_regions_live_at<T>(&mut self, value: T, location: Location)
|
||||
where
|
||||
T: TypeVisitable<TyCtxt<'tcx>>,
|
||||
{
|
||||
debug!("record_regions_live_at(value={:?}, location={:?})", value, location);
|
||||
self.tcx.for_each_free_region(&value, |live_region| {
|
||||
let live_region_vid = live_region.as_var();
|
||||
self.liveness_constraints.add_location(live_region_vid, location);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ pub(super) fn populate_access_facts<'a, 'tcx>(
|
||||
location_table,
|
||||
move_data,
|
||||
};
|
||||
extractor.visit_body(&body);
|
||||
extractor.visit_body(body);
|
||||
|
||||
facts.var_dropped_at.extend(
|
||||
dropped_at.iter().map(|&(local, location)| (local, location_table.mid_index(location))),
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::WithSuccessors;
|
||||
use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
|
||||
use rustc_index::bit_set::HybridBitSet;
|
||||
use rustc_index::interval::IntervalSet;
|
||||
use rustc_infer::infer::canonical::QueryRegionConstraints;
|
||||
use rustc_infer::infer::outlives::for_liveness;
|
||||
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
|
||||
use rustc_middle::traits::query::DropckOutlivesResult;
|
||||
use rustc_middle::ty::{RegionVid, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
|
||||
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
|
||||
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
|
||||
@ -16,9 +16,8 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
|
||||
use crate::dataflow::BorrowIndex;
|
||||
use crate::{
|
||||
region_infer::values::{self, PointIndex, RegionValueElements},
|
||||
region_infer::values::{self, LiveLoans, PointIndex, RegionValueElements},
|
||||
type_check::liveness::local_use_map::LocalUseMap,
|
||||
type_check::liveness::polonius,
|
||||
type_check::NormalizeLocation,
|
||||
@ -49,22 +48,17 @@ pub(super) fn trace<'mir, 'tcx>(
|
||||
boring_locals: Vec<Local>,
|
||||
polonius_drop_used: Option<Vec<(Local, Location)>>,
|
||||
) {
|
||||
debug!("trace()");
|
||||
|
||||
let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body);
|
||||
|
||||
// When using `-Zpolonius=next`, compute the set of loans that can reach a given region.
|
||||
let num_loans = typeck.borrowck_context.borrow_set.len();
|
||||
let mut inflowing_loans = SparseBitMatrix::new(num_loans);
|
||||
if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
let borrowck_context = &typeck.borrowck_context;
|
||||
let borrowck_context = &mut typeck.borrowck_context;
|
||||
let borrow_set = &borrowck_context.borrow_set;
|
||||
let constraint_set = &borrowck_context.constraints.outlives_constraints;
|
||||
|
||||
let num_region_vars = typeck.infcx.num_region_vars();
|
||||
let graph = constraint_set.graph(num_region_vars);
|
||||
let mut live_loans = LiveLoans::new(borrow_set.len());
|
||||
let outlives_constraints = &borrowck_context.constraints.outlives_constraints;
|
||||
let graph = outlives_constraints.graph(typeck.infcx.num_region_vars());
|
||||
let region_graph =
|
||||
graph.region_graph(&constraint_set, borrowck_context.universal_regions.fr_static);
|
||||
graph.region_graph(outlives_constraints, borrowck_context.universal_regions.fr_static);
|
||||
|
||||
// Traverse each issuing region's constraints, and record the loan as flowing into the
|
||||
// outlived region.
|
||||
@ -75,9 +69,13 @@ pub(super) fn trace<'mir, 'tcx>(
|
||||
continue;
|
||||
}
|
||||
|
||||
inflowing_loans.insert(succ, loan);
|
||||
live_loans.inflowing_loans.insert(succ, loan);
|
||||
}
|
||||
}
|
||||
|
||||
// Store the inflowing loans in the liveness constraints: they will be used to compute live
|
||||
// loans when liveness data is recorded there.
|
||||
borrowck_context.constraints.liveness_constraints.loans = Some(live_loans);
|
||||
};
|
||||
|
||||
let cx = LivenessContext {
|
||||
@ -88,7 +86,6 @@ pub(super) fn trace<'mir, 'tcx>(
|
||||
local_use_map,
|
||||
move_data,
|
||||
drop_data: FxIndexMap::default(),
|
||||
inflowing_loans,
|
||||
};
|
||||
|
||||
let mut results = LivenessResults::new(cx);
|
||||
@ -126,9 +123,6 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
|
||||
/// Index indicating where each variable is assigned, used, or
|
||||
/// dropped.
|
||||
local_use_map: &'me LocalUseMap,
|
||||
|
||||
/// Set of loans that flow into a given region, when using `-Zpolonius=next`.
|
||||
inflowing_loans: SparseBitMatrix<RegionVid, BorrowIndex>,
|
||||
}
|
||||
|
||||
struct DropData<'tcx> {
|
||||
@ -489,7 +483,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
||||
}
|
||||
|
||||
let move_paths = &self.flow_inits.analysis().move_data().move_paths;
|
||||
move_paths[mpi].find_descendant(&move_paths, |mpi| state.contains(mpi)).is_some()
|
||||
move_paths[mpi].find_descendant(move_paths, |mpi| state.contains(mpi)).is_some()
|
||||
}
|
||||
|
||||
/// Returns `true` if the local variable (or some part of it) is initialized in
|
||||
@ -519,14 +513,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
||||
live_at: &IntervalSet<PointIndex>,
|
||||
) {
|
||||
debug!("add_use_live_facts_for(value={:?})", value);
|
||||
|
||||
Self::make_all_regions_live(
|
||||
self.elements,
|
||||
&mut self.typeck,
|
||||
value,
|
||||
live_at,
|
||||
&self.inflowing_loans,
|
||||
);
|
||||
Self::make_all_regions_live(self.elements, self.typeck, value, live_at);
|
||||
}
|
||||
|
||||
/// Some variable with type `live_ty` is "drop live" at `location`
|
||||
@ -550,7 +537,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
||||
dropped_local,
|
||||
dropped_ty,
|
||||
drop_locations,
|
||||
values::location_set_str(self.elements, live_at.iter()),
|
||||
values::pretty_print_points(self.elements, live_at.iter()),
|
||||
);
|
||||
|
||||
let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
|
||||
@ -577,15 +564,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
||||
// All things in the `outlives` array may be touched by
|
||||
// the destructor and must be live at this point.
|
||||
for &kind in &drop_data.dropck_result.kinds {
|
||||
Self::make_all_regions_live(
|
||||
self.elements,
|
||||
&mut self.typeck,
|
||||
kind,
|
||||
live_at,
|
||||
&self.inflowing_loans,
|
||||
);
|
||||
|
||||
polonius::add_drop_of_var_derefs_origin(&mut self.typeck, dropped_local, &kind);
|
||||
Self::make_all_regions_live(self.elements, self.typeck, kind, live_at);
|
||||
polonius::add_drop_of_var_derefs_origin(self.typeck, dropped_local, &kind);
|
||||
}
|
||||
}
|
||||
|
||||
@ -594,20 +574,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
value: impl TypeVisitable<TyCtxt<'tcx>>,
|
||||
live_at: &IntervalSet<PointIndex>,
|
||||
inflowing_loans: &SparseBitMatrix<RegionVid, BorrowIndex>,
|
||||
) {
|
||||
debug!("make_all_regions_live(value={:?})", value);
|
||||
debug!(
|
||||
"make_all_regions_live: live_at={}",
|
||||
values::location_set_str(elements, live_at.iter()),
|
||||
values::pretty_print_points(elements, live_at.iter()),
|
||||
);
|
||||
|
||||
// When using `-Zpolonius=next`, we want to record the loans that flow into this value's
|
||||
// regions as being live at the given `live_at` points: this will be used to compute the
|
||||
// location where a loan goes out of scope.
|
||||
let num_loans = typeck.borrowck_context.borrow_set.len();
|
||||
let value_loans = &mut HybridBitSet::new_empty(num_loans);
|
||||
|
||||
value.visit_with(&mut for_liveness::FreeRegionsVisitor {
|
||||
tcx: typeck.tcx(),
|
||||
param_env: typeck.param_env,
|
||||
@ -618,22 +591,9 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
||||
.borrowck_context
|
||||
.constraints
|
||||
.liveness_constraints
|
||||
.add_elements(live_region_vid, live_at);
|
||||
|
||||
// There can only be inflowing loans for this region when we are using
|
||||
// `-Zpolonius=next`.
|
||||
if let Some(inflowing) = inflowing_loans.row(live_region_vid) {
|
||||
value_loans.union(inflowing);
|
||||
}
|
||||
.add_points(live_region_vid, live_at);
|
||||
},
|
||||
});
|
||||
|
||||
// Record the loans reaching the value.
|
||||
if !value_loans.is_empty() {
|
||||
for point in live_at.iter() {
|
||||
typeck.borrowck_context.live_loans.union_row(point, value_loans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_drop_data(
|
||||
|
||||
@ -14,18 +14,16 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::bit_set::SparseBitMatrix;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_infer::infer::canonical::QueryRegionConstraints;
|
||||
use rustc_infer::infer::outlives::env::RegionBoundPairs;
|
||||
use rustc_infer::infer::region_constraints::RegionConstraintData;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::{
|
||||
InferCtxt, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin,
|
||||
BoundRegion, BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin,
|
||||
};
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
@ -51,8 +49,6 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
|
||||
use crate::dataflow::BorrowIndex;
|
||||
use crate::region_infer::values::PointIndex;
|
||||
use crate::session_diagnostics::{MoveUnsized, SimdShuffleLastConst};
|
||||
use crate::{
|
||||
borrow_set::BorrowSet,
|
||||
@ -68,7 +64,7 @@ use crate::{
|
||||
region_infer::TypeTest,
|
||||
type_check::free_region_relations::{CreateResult, UniversalRegionRelations},
|
||||
universal_regions::{DefiningTy, UniversalRegions},
|
||||
BorrowckInferCtxt, Upvar,
|
||||
BorrowckInferCtxt,
|
||||
};
|
||||
|
||||
macro_rules! span_mirbug {
|
||||
@ -138,7 +134,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
||||
flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
elements: &Rc<RegionValueElements>,
|
||||
upvars: &[Upvar<'tcx>],
|
||||
upvars: &[&ty::CapturedPlace<'tcx>],
|
||||
use_polonius: bool,
|
||||
) -> MirTypeckResults<'tcx> {
|
||||
let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body);
|
||||
@ -166,9 +162,6 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
||||
|
||||
debug!(?normalized_inputs_and_output);
|
||||
|
||||
// When using `-Zpolonius=next`, liveness will record the set of live loans per point.
|
||||
let mut live_loans = SparseBitMatrix::new(borrow_set.len());
|
||||
|
||||
let mut borrowck_context = BorrowCheckContext {
|
||||
universal_regions,
|
||||
location_table,
|
||||
@ -176,7 +169,6 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
||||
all_facts,
|
||||
constraints: &mut constraints,
|
||||
upvars,
|
||||
live_loans: &mut live_loans,
|
||||
};
|
||||
|
||||
let mut checker = TypeChecker::new(
|
||||
@ -191,11 +183,11 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
||||
checker.check_user_type_annotations();
|
||||
|
||||
let mut verifier = TypeVerifier::new(&mut checker, promoted);
|
||||
verifier.visit_body(&body);
|
||||
verifier.visit_body(body);
|
||||
|
||||
checker.typeck_mir(body);
|
||||
checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output);
|
||||
checker.check_signature_annotation(&body);
|
||||
checker.equate_inputs_and_outputs(body, universal_regions, &normalized_inputs_and_output);
|
||||
checker.check_signature_annotation(body);
|
||||
|
||||
liveness::generate(
|
||||
&mut checker,
|
||||
@ -232,7 +224,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
||||
let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
|
||||
trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
|
||||
if hidden_type.has_non_region_infer() {
|
||||
let reported = infcx.tcx.sess.delay_span_bug(
|
||||
let reported = infcx.tcx.sess.span_delayed_bug(
|
||||
decl.hidden_type.span,
|
||||
format!("could not resolve {:#?}", hidden_type.ty.kind()),
|
||||
);
|
||||
@ -243,7 +235,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
||||
})
|
||||
.collect();
|
||||
|
||||
MirTypeckResults { constraints, universal_region_relations, opaque_type_values, live_loans }
|
||||
MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
|
||||
}
|
||||
|
||||
fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
|
||||
@ -274,9 +266,9 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
|
||||
#[track_caller]
|
||||
fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: String) {
|
||||
// We sometimes see MIR failures (notably predicate failures) due to
|
||||
// the fact that we check rvalue sized predicates here. So use `delay_span_bug`
|
||||
// the fact that we check rvalue sized predicates here. So use `span_delayed_bug`
|
||||
// to avoid reporting bugs in those cases.
|
||||
tcx.sess.diagnostic().delay_span_bug(span, msg);
|
||||
tcx.sess.dcx().span_delayed_bug(span, msg);
|
||||
}
|
||||
|
||||
enum FieldAccessError {
|
||||
@ -318,7 +310,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
||||
.borrowck_context
|
||||
.constraints
|
||||
.liveness_constraints
|
||||
.add_element(live_region_vid, location);
|
||||
.add_location(live_region_vid, location);
|
||||
});
|
||||
|
||||
// HACK(compiler-errors): Constants that are gathered into Body.required_consts
|
||||
@ -389,7 +381,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
||||
self.cx.ascribe_user_type(
|
||||
constant.const_.ty(),
|
||||
UserType::TypeOf(uv.def, UserArgs { args: uv.args, user_self_ty: None }),
|
||||
locations.span(&self.cx.body),
|
||||
locations.span(self.cx.body),
|
||||
);
|
||||
}
|
||||
} else if let Some(static_def_id) = constant.check_static_ptr(tcx) {
|
||||
@ -553,7 +545,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
let all_facts = &mut None;
|
||||
let mut constraints = Default::default();
|
||||
let mut liveness_constraints =
|
||||
LivenessValues::new(Rc::new(RegionValueElements::new(&promoted_body)));
|
||||
LivenessValues::new(Rc::new(RegionValueElements::new(promoted_body)));
|
||||
// Don't try to add borrow_region facts for the promoted MIR
|
||||
|
||||
let mut swap_constraints = |this: &mut Self| {
|
||||
@ -570,7 +562,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
|
||||
swap_constraints(self);
|
||||
|
||||
self.visit_body(&promoted_body);
|
||||
self.visit_body(promoted_body);
|
||||
|
||||
self.cx.typeck_mir(promoted_body);
|
||||
|
||||
@ -592,16 +584,16 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
}
|
||||
self.cx.borrowck_context.constraints.outlives_constraints.push(constraint)
|
||||
}
|
||||
for region in liveness_constraints.rows() {
|
||||
for region in liveness_constraints.regions() {
|
||||
// If the region is live at at least one location in the promoted MIR,
|
||||
// then add a liveness constraint to the main MIR for this region
|
||||
// at the location provided as an argument to this method
|
||||
if liveness_constraints.get_elements(region).next().is_some() {
|
||||
if liveness_constraints.is_live_anywhere(region) {
|
||||
self.cx
|
||||
.borrowck_context
|
||||
.constraints
|
||||
.liveness_constraints
|
||||
.add_element(region, location);
|
||||
.add_location(region, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -857,11 +849,7 @@ struct BorrowCheckContext<'a, 'tcx> {
|
||||
all_facts: &'a mut Option<AllFacts>,
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
pub(crate) constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
|
||||
upvars: &'a [Upvar<'tcx>],
|
||||
|
||||
/// The set of loans that are live at a given point in the CFG, filled in by `liveness::trace`,
|
||||
/// when using `-Zpolonius=next`.
|
||||
pub(crate) live_loans: &'a mut SparseBitMatrix<PointIndex, BorrowIndex>,
|
||||
upvars: &'a [&'a ty::CapturedPlace<'tcx>],
|
||||
}
|
||||
|
||||
/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions
|
||||
@ -870,9 +858,6 @@ pub(crate) struct MirTypeckResults<'tcx> {
|
||||
pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
|
||||
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
|
||||
pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
|
||||
|
||||
/// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`.
|
||||
pub(crate) live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
|
||||
}
|
||||
|
||||
/// A collection of region constraints that must be satisfied for the
|
||||
@ -899,7 +884,7 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> {
|
||||
/// not otherwise appear in the MIR -- in particular, the
|
||||
/// late-bound regions that it instantiates at call-sites -- and
|
||||
/// hence it must report on their liveness constraints.
|
||||
pub(crate) liveness_constraints: LivenessValues<RegionVid>,
|
||||
pub(crate) liveness_constraints: LivenessValues,
|
||||
|
||||
pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>,
|
||||
|
||||
@ -1018,16 +1003,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
reported_errors: Default::default(),
|
||||
};
|
||||
|
||||
// FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
|
||||
// FIXME(-Znext-solver): A bit dubious that we're only registering
|
||||
// predefined opaques in the typeck root.
|
||||
if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
|
||||
checker.register_predefined_opaques_in_new_solver();
|
||||
checker.register_predefined_opaques_for_next_solver();
|
||||
}
|
||||
|
||||
checker
|
||||
}
|
||||
|
||||
pub(super) fn register_predefined_opaques_in_new_solver(&mut self) {
|
||||
pub(super) fn register_predefined_opaques_for_next_solver(&mut self) {
|
||||
// OK to use the identity arguments for each opaque type key, since
|
||||
// we remap opaques from HIR typeck back to their definition params.
|
||||
let opaques: Vec<_> = self
|
||||
@ -1082,7 +1067,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
);
|
||||
|
||||
if result.is_err() {
|
||||
self.infcx.tcx.sess.delay_span_bug(
|
||||
self.infcx.tcx.sess.span_delayed_bug(
|
||||
self.body.span,
|
||||
"failed re-defining predefined opaques in mir typeck",
|
||||
);
|
||||
@ -1127,7 +1112,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
locations,
|
||||
locations.span(self.body),
|
||||
category,
|
||||
&mut self.borrowck_context.constraints,
|
||||
self.borrowck_context.constraints,
|
||||
)
|
||||
.convert_all(data);
|
||||
}
|
||||
@ -1202,7 +1187,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
#[instrument(skip(self, body, location), level = "debug")]
|
||||
#[instrument(skip(self, body), level = "debug")]
|
||||
fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) {
|
||||
let tcx = self.tcx();
|
||||
debug!("stmt kind: {:?}", stmt.kind);
|
||||
@ -1387,7 +1372,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let (sig, map) = tcx.replace_late_bound_regions(sig, |br| {
|
||||
let (sig, map) = tcx.instantiate_bound_regions(sig, |br| {
|
||||
use crate::renumber::RegionCtxt;
|
||||
|
||||
let region_ctxt_fn = || {
|
||||
@ -1401,10 +1386,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
};
|
||||
|
||||
self.infcx.next_region_var(
|
||||
LateBoundRegion(
|
||||
BoundRegion(
|
||||
term.source_info.span,
|
||||
br.kind,
|
||||
LateBoundRegionConversionTime::FnCall,
|
||||
BoundRegionConversionTime::FnCall,
|
||||
),
|
||||
region_ctxt_fn,
|
||||
)
|
||||
@ -1443,7 +1428,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
self.borrowck_context
|
||||
.constraints
|
||||
.liveness_constraints
|
||||
.add_element(region_vid, term_location);
|
||||
.add_location(region_vid, term_location);
|
||||
}
|
||||
|
||||
self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source);
|
||||
@ -1854,7 +1839,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
for op in ops {
|
||||
self.check_operand(op, location);
|
||||
}
|
||||
self.check_aggregate_rvalue(&body, rvalue, ak, ops, location)
|
||||
self.check_aggregate_rvalue(body, rvalue, ak, ops, location)
|
||||
}
|
||||
|
||||
Rvalue::Repeat(operand, len) => {
|
||||
@ -1934,7 +1919,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty,
|
||||
ty_fn_ptr_from,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -1959,7 +1944,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty,
|
||||
ty_fn_ptr_from,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -1988,7 +1973,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty,
|
||||
ty_fn_ptr_from,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -2013,7 +1998,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast {
|
||||
unsize_to: Some(tcx.fold_regions(ty, |r, _| {
|
||||
if let ty::ReVar(_) = r.kind() {
|
||||
tcx.lifetimes.re_erased
|
||||
} else {
|
||||
r
|
||||
}
|
||||
})),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -2033,7 +2026,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
.iter()
|
||||
.map(|predicate| predicate.with_self_ty(tcx, self_ty)),
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
);
|
||||
|
||||
let outlives_predicate = tcx.mk_predicate(Binder::dummy(
|
||||
@ -2044,7 +2037,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
self.prove_predicate(
|
||||
outlives_predicate,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
);
|
||||
}
|
||||
|
||||
@ -2065,7 +2058,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty_from,
|
||||
*ty_to,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -2131,7 +2124,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty_elem,
|
||||
*ty_to,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -2292,7 +2285,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
|
||||
Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
|
||||
self.add_reborrow_constraint(&body, location, *region, borrowed_place);
|
||||
self.add_reborrow_constraint(body, location, *region, borrowed_place);
|
||||
}
|
||||
|
||||
Rvalue::BinaryOp(
|
||||
@ -2504,7 +2497,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
let tcx = self.infcx.tcx;
|
||||
let field = path_utils::is_upvar_field_projection(
|
||||
tcx,
|
||||
&self.borrowck_context.upvars,
|
||||
self.borrowck_context.upvars,
|
||||
borrowed_place.as_ref(),
|
||||
body,
|
||||
);
|
||||
@ -2660,13 +2653,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
location.to_locations(),
|
||||
DUMMY_SP, // irrelevant; will be overridden.
|
||||
ConstraintCategory::Boring, // same as above.
|
||||
&mut self.borrowck_context.constraints,
|
||||
self.borrowck_context.constraints,
|
||||
)
|
||||
.apply_closure_requirements(
|
||||
&closure_requirements,
|
||||
def_id.to_def_id(),
|
||||
args,
|
||||
);
|
||||
.apply_closure_requirements(closure_requirements, def_id.to_def_id(), args);
|
||||
}
|
||||
|
||||
// Now equate closure args to regions inherited from `typeck_root_def_id`. Fixes #98589.
|
||||
@ -2674,8 +2663,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id);
|
||||
|
||||
let parent_args = match tcx.def_kind(def_id) {
|
||||
DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => {
|
||||
args.as_coroutine().parent_args()
|
||||
}
|
||||
DefKind::Closure => args.as_closure().parent_args(),
|
||||
DefKind::Coroutine => args.as_coroutine().parent_args(),
|
||||
DefKind::InlineConst => args.as_inline_const().parent_args(),
|
||||
other => bug!("unexpected item {:?}", other),
|
||||
};
|
||||
@ -2706,7 +2697,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
debug!(?body.span);
|
||||
|
||||
for (local, local_decl) in body.local_decls.iter_enumerated() {
|
||||
self.check_local(&body, local, local_decl);
|
||||
self.check_local(body, local, local_decl);
|
||||
}
|
||||
|
||||
for (block, block_data) in body.basic_blocks.iter_enumerated() {
|
||||
@ -2719,8 +2710,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
location.statement_index += 1;
|
||||
}
|
||||
|
||||
self.check_terminator(&body, block_data.terminator(), location);
|
||||
self.check_iscleanup(&body, block_data);
|
||||
self.check_terminator(body, block_data.terminator(), location);
|
||||
self.check_iscleanup(body, block_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,12 +107,12 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
|
||||
fn next_existential_region_var(
|
||||
&mut self,
|
||||
from_forall: bool,
|
||||
_name: Option<Symbol>,
|
||||
name: Option<Symbol>,
|
||||
) -> ty::Region<'tcx> {
|
||||
let origin = NllRegionVariableOrigin::Existential { from_forall };
|
||||
|
||||
let reg_var =
|
||||
self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(_name));
|
||||
self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name));
|
||||
|
||||
reg_var
|
||||
}
|
||||
|
||||
@ -462,7 +462,6 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
||||
|
||||
// "Liberate" the late-bound regions. These correspond to
|
||||
// "local" free regions.
|
||||
|
||||
let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
|
||||
|
||||
let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars(
|
||||
@ -665,7 +664,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
||||
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
kind: ty::BrEnv,
|
||||
};
|
||||
let env_region = ty::Region::new_late_bound(tcx, ty::INNERMOST, br);
|
||||
let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br);
|
||||
let closure_ty = tcx.closure_env_ty(def_id, args, env_region).unwrap();
|
||||
|
||||
// The "inputs" of the closure in the
|
||||
@ -738,18 +737,6 @@ trait InferCtxtExt<'tcx> {
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>>;
|
||||
|
||||
fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope(
|
||||
&self,
|
||||
mir_def_id: LocalDefId,
|
||||
indices: &mut UniversalRegionIndices<'tcx>,
|
||||
);
|
||||
|
||||
fn replace_late_bound_regions_with_nll_infer_vars_in_item(
|
||||
&self,
|
||||
mir_def_id: LocalDefId,
|
||||
indices: &mut UniversalRegionIndices<'tcx>,
|
||||
);
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> {
|
||||
@ -781,10 +768,10 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> {
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
let (value, _map) = self.tcx.replace_late_bound_regions(value, |br| {
|
||||
let (value, _map) = self.tcx.instantiate_bound_regions(value, |br| {
|
||||
debug!(?br);
|
||||
let liberated_region =
|
||||
ty::Region::new_free(self.tcx, all_outlive_scope.to_def_id(), br.kind);
|
||||
ty::Region::new_late_param(self.tcx, all_outlive_scope.to_def_id(), br.kind);
|
||||
let region_vid = {
|
||||
let name = match br.kind.get_name() {
|
||||
Some(name) => name,
|
||||
@ -800,61 +787,13 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> {
|
||||
});
|
||||
value
|
||||
}
|
||||
|
||||
/// Finds late-bound regions that do not appear in the parameter listing and adds them to the
|
||||
/// indices vector. Typically, we identify late-bound regions as we process the inputs and
|
||||
/// outputs of the closure/function. However, sometimes there are late-bound regions which do
|
||||
/// not appear in the fn parameters but which are nonetheless in scope. The simplest case of
|
||||
/// this are unused functions, like fn foo<'a>() { } (see e.g., #51351). Despite not being used,
|
||||
/// users can still reference these regions (e.g., let x: &'a u32 = &22;), so we need to create
|
||||
/// entries for them and store them in the indices map. This code iterates over the complete
|
||||
/// set of late-bound regions and checks for any that we have not yet seen, adding them to the
|
||||
/// inputs vector.
|
||||
#[instrument(skip(self, indices))]
|
||||
fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope(
|
||||
&self,
|
||||
mir_def_id: LocalDefId,
|
||||
indices: &mut UniversalRegionIndices<'tcx>,
|
||||
) {
|
||||
for_each_late_bound_region_in_recursive_scope(self.tcx, mir_def_id, |r| {
|
||||
debug!(?r);
|
||||
if !indices.indices.contains_key(&r) {
|
||||
let region_vid = {
|
||||
let name = r.get_name_or_anon();
|
||||
self.next_nll_region_var(FR, || RegionCtxt::LateBound(name))
|
||||
};
|
||||
|
||||
debug!(?region_vid);
|
||||
indices.insert_late_bound_region(r, region_vid.as_var());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(skip(self, indices))]
|
||||
fn replace_late_bound_regions_with_nll_infer_vars_in_item(
|
||||
&self,
|
||||
mir_def_id: LocalDefId,
|
||||
indices: &mut UniversalRegionIndices<'tcx>,
|
||||
) {
|
||||
for_each_late_bound_region_in_item(self.tcx, mir_def_id, |r| {
|
||||
debug!(?r);
|
||||
if !indices.indices.contains_key(&r) {
|
||||
let region_vid = {
|
||||
let name = r.get_name_or_anon();
|
||||
self.next_nll_region_var(FR, || RegionCtxt::LateBound(name))
|
||||
};
|
||||
|
||||
indices.insert_late_bound_region(r, region_vid.as_var());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UniversalRegionIndices<'tcx> {
|
||||
/// Initially, the `UniversalRegionIndices` map contains only the
|
||||
/// early-bound regions in scope. Once that is all setup, we come
|
||||
/// in later and instantiate the late-bound regions, and then we
|
||||
/// insert the `ReFree` version of those into the map as
|
||||
/// insert the `ReLateParam` version of those into the map as
|
||||
/// well. These are used for error reporting.
|
||||
fn insert_late_bound_region(&mut self, r: ty::Region<'tcx>, vid: ty::RegionVid) {
|
||||
debug!("insert_late_bound_region({:?}, {:?})", r, vid);
|
||||
@ -929,11 +868,12 @@ fn for_each_late_bound_region_in_item<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
for bound_var in tcx.late_bound_vars(tcx.hir().local_def_id_to_hir_id(mir_def_id)) {
|
||||
for bound_var in tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)) {
|
||||
let ty::BoundVariableKind::Region(bound_region) = bound_var else {
|
||||
continue;
|
||||
};
|
||||
let liberated_region = ty::Region::new_free(tcx, mir_def_id.to_def_id(), bound_region);
|
||||
let liberated_region =
|
||||
ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), bound_region);
|
||||
f(liberated_region);
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
never_initialized_mut_locals: &mut never_initialized_mut_locals,
|
||||
mbcx: self,
|
||||
};
|
||||
visitor.visit_body(&visitor.mbcx.body);
|
||||
visitor.visit_body(visitor.mbcx.body);
|
||||
}
|
||||
|
||||
// Take the union of the existed `used_mut` set with those variables we've found were
|
||||
|
||||
@ -31,10 +31,7 @@ pub fn expand(
|
||||
{
|
||||
(item, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
|
||||
} else {
|
||||
ecx.sess
|
||||
.parse_sess
|
||||
.span_diagnostic
|
||||
.emit_err(errors::AllocErrorMustBeFn { span: item.span() });
|
||||
ecx.sess.dcx().emit_err(errors::AllocErrorMustBeFn { span: item.span() });
|
||||
return vec![orig_item];
|
||||
};
|
||||
|
||||
|
||||
@ -47,10 +47,10 @@ pub fn parse_asm_args<'a>(
|
||||
sp: Span,
|
||||
is_global_asm: bool,
|
||||
) -> PResult<'a, AsmArgs> {
|
||||
let diag = &sess.span_diagnostic;
|
||||
let dcx = &sess.dcx;
|
||||
|
||||
if p.token == token::Eof {
|
||||
return Err(diag.create_err(errors::AsmRequiresTemplate { span: sp }));
|
||||
return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
|
||||
}
|
||||
|
||||
let first_template = p.parse_expr()?;
|
||||
@ -69,7 +69,7 @@ pub fn parse_asm_args<'a>(
|
||||
if !p.eat(&token::Comma) {
|
||||
if allow_templates {
|
||||
// After a template string, we always expect *only* a comma...
|
||||
return Err(diag.create_err(errors::AsmExpectedComma { span: p.token.span }));
|
||||
return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
|
||||
} else {
|
||||
// ...after that delegate to `expect` to also include the other expected tokens.
|
||||
return Err(p.expect(&token::Comma).err().unwrap());
|
||||
@ -110,7 +110,7 @@ pub fn parse_asm_args<'a>(
|
||||
let op = if !is_global_asm && p.eat_keyword(kw::In) {
|
||||
let reg = parse_reg(p, &mut explicit_reg)?;
|
||||
if p.eat_keyword(kw::Underscore) {
|
||||
let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
@ -126,7 +126,7 @@ pub fn parse_asm_args<'a>(
|
||||
} else if !is_global_asm && p.eat_keyword(sym::inout) {
|
||||
let reg = parse_reg(p, &mut explicit_reg)?;
|
||||
if p.eat_keyword(kw::Underscore) {
|
||||
let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
@ -140,7 +140,7 @@ pub fn parse_asm_args<'a>(
|
||||
} else if !is_global_asm && p.eat_keyword(sym::inlateout) {
|
||||
let reg = parse_reg(p, &mut explicit_reg)?;
|
||||
if p.eat_keyword(kw::Underscore) {
|
||||
let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
@ -157,7 +157,7 @@ pub fn parse_asm_args<'a>(
|
||||
} else if p.eat_keyword(sym::sym) {
|
||||
let expr = p.parse_expr()?;
|
||||
let ast::ExprKind::Path(qself, path) = &expr.kind else {
|
||||
let err = diag.create_err(errors::AsmSymNoPath { span: expr.span });
|
||||
let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
|
||||
return Err(err);
|
||||
};
|
||||
let sym = ast::InlineAsmSym {
|
||||
@ -178,7 +178,7 @@ pub fn parse_asm_args<'a>(
|
||||
) => {}
|
||||
ast::ExprKind::MacCall(..) => {}
|
||||
_ => {
|
||||
let err = diag.create_err(errors::AsmExpectedOther {
|
||||
let err = dcx.create_err(errors::AsmExpectedOther {
|
||||
span: template.span,
|
||||
is_global_asm,
|
||||
});
|
||||
@ -201,12 +201,12 @@ pub fn parse_asm_args<'a>(
|
||||
// of the argument available.
|
||||
if explicit_reg {
|
||||
if name.is_some() {
|
||||
diag.emit_err(errors::AsmExplicitRegisterName { span });
|
||||
dcx.emit_err(errors::AsmExplicitRegisterName { span });
|
||||
}
|
||||
args.reg_args.insert(slot);
|
||||
} else if let Some(name) = name {
|
||||
if let Some(&prev) = args.named_args.get(&name) {
|
||||
diag.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
|
||||
dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
|
||||
continue;
|
||||
}
|
||||
args.named_args.insert(name, slot);
|
||||
@ -215,7 +215,7 @@ pub fn parse_asm_args<'a>(
|
||||
let named = args.named_args.values().map(|p| args.operands[*p].1).collect();
|
||||
let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect();
|
||||
|
||||
diag.emit_err(errors::AsmPositionalAfter { span, named, explicit });
|
||||
dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -224,19 +224,19 @@ pub fn parse_asm_args<'a>(
|
||||
&& args.options.contains(ast::InlineAsmOptions::READONLY)
|
||||
{
|
||||
let spans = args.options_spans.clone();
|
||||
diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
|
||||
dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
|
||||
}
|
||||
if args.options.contains(ast::InlineAsmOptions::PURE)
|
||||
&& args.options.contains(ast::InlineAsmOptions::NORETURN)
|
||||
{
|
||||
let spans = args.options_spans.clone();
|
||||
diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
|
||||
dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
|
||||
}
|
||||
if args.options.contains(ast::InlineAsmOptions::PURE)
|
||||
&& !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
|
||||
{
|
||||
let spans = args.options_spans.clone();
|
||||
diag.emit_err(errors::AsmPureCombine { spans });
|
||||
dcx.emit_err(errors::AsmPureCombine { spans });
|
||||
}
|
||||
|
||||
let mut have_real_output = false;
|
||||
@ -263,17 +263,17 @@ pub fn parse_asm_args<'a>(
|
||||
}
|
||||
}
|
||||
if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
|
||||
diag.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
|
||||
dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
|
||||
}
|
||||
if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
|
||||
let err = diag.create_err(errors::AsmNoReturn { outputs_sp });
|
||||
let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
|
||||
// Bail out now since this is likely to confuse MIR
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
if args.clobber_abis.len() > 0 {
|
||||
if is_global_asm {
|
||||
let err = diag.create_err(errors::GlobalAsmClobberAbi {
|
||||
let err = dcx.create_err(errors::GlobalAsmClobberAbi {
|
||||
spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
|
||||
});
|
||||
|
||||
@ -281,7 +281,7 @@ pub fn parse_asm_args<'a>(
|
||||
return Err(err);
|
||||
}
|
||||
if !regclass_outputs.is_empty() {
|
||||
diag.emit_err(errors::AsmClobberNoReg {
|
||||
dcx.emit_err(errors::AsmClobberNoReg {
|
||||
spans: regclass_outputs,
|
||||
clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
|
||||
});
|
||||
@ -298,7 +298,7 @@ pub fn parse_asm_args<'a>(
|
||||
fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
|
||||
// Tool-only output
|
||||
let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span };
|
||||
p.sess.span_diagnostic.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
|
||||
p.sess.dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
|
||||
}
|
||||
|
||||
/// Try to set the provided option in the provided `AsmArgs`.
|
||||
@ -370,7 +370,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
|
||||
p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
|
||||
|
||||
if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
|
||||
return Err(p.sess.span_diagnostic.create_err(errors::NonABI { span: p.token.span }));
|
||||
return Err(p.sess.dcx.create_err(errors::NonABI { span: p.token.span }));
|
||||
}
|
||||
|
||||
let mut new_abis = Vec::new();
|
||||
@ -381,8 +381,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
|
||||
}
|
||||
Err(opt_lit) => {
|
||||
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
|
||||
let mut err =
|
||||
p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
|
||||
let mut err = p.sess.dcx.struct_span_err(span, "expected string literal");
|
||||
err.span_label(span, "not a string literal");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
||||
fn build_panic(&self, expr_str: &str, panic_path: Path) -> P<Expr> {
|
||||
let escaped_expr_str = escape_to_fmt(expr_str);
|
||||
let initial = [
|
||||
TokenTree::token_alone(
|
||||
TokenTree::token_joint_hidden(
|
||||
token::Literal(token::Lit {
|
||||
kind: token::LitKind::Str,
|
||||
symbol: Symbol::intern(&if self.fmt_string.is_empty() {
|
||||
@ -170,7 +170,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
||||
];
|
||||
let captures = self.capture_decls.iter().flat_map(|cap| {
|
||||
[
|
||||
TokenTree::token_alone(token::Ident(cap.ident.name, false), cap.ident.span),
|
||||
TokenTree::token_joint_hidden(token::Ident(cap.ident.name, false), cap.ident.span),
|
||||
TokenTree::token_alone(token::Comma, self.span),
|
||||
]
|
||||
});
|
||||
|
||||
@ -47,7 +47,7 @@ impl MultiItemModifier for Expander {
|
||||
let template = AttributeTemplate { list: Some("path"), ..Default::default() };
|
||||
validate_attr::check_builtin_meta_item(
|
||||
&ecx.sess.parse_sess,
|
||||
&meta_item,
|
||||
meta_item,
|
||||
ast::AttrStyle::Outer,
|
||||
sym::cfg_accessible,
|
||||
template,
|
||||
|
||||
@ -25,7 +25,7 @@ pub(crate) fn expand(
|
||||
annotatable: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval);
|
||||
warn_on_duplicate_attribute(&ecx, &annotatable, sym::cfg_eval);
|
||||
warn_on_duplicate_attribute(ecx, &annotatable, sym::cfg_eval);
|
||||
vec![cfg_eval(ecx.sess, ecx.ecfg.features, annotatable, ecx.current_expansion.lint_node_id)]
|
||||
}
|
||||
|
||||
@ -95,19 +95,19 @@ impl CfgFinder {
|
||||
fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool {
|
||||
let mut finder = CfgFinder { has_cfg_or_cfg_attr: false };
|
||||
match annotatable {
|
||||
Annotatable::Item(item) => finder.visit_item(&item),
|
||||
Annotatable::TraitItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Trait),
|
||||
Annotatable::ImplItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Impl),
|
||||
Annotatable::ForeignItem(item) => finder.visit_foreign_item(&item),
|
||||
Annotatable::Stmt(stmt) => finder.visit_stmt(&stmt),
|
||||
Annotatable::Expr(expr) => finder.visit_expr(&expr),
|
||||
Annotatable::Arm(arm) => finder.visit_arm(&arm),
|
||||
Annotatable::ExprField(field) => finder.visit_expr_field(&field),
|
||||
Annotatable::PatField(field) => finder.visit_pat_field(&field),
|
||||
Annotatable::GenericParam(param) => finder.visit_generic_param(¶m),
|
||||
Annotatable::Param(param) => finder.visit_param(¶m),
|
||||
Annotatable::FieldDef(field) => finder.visit_field_def(&field),
|
||||
Annotatable::Variant(variant) => finder.visit_variant(&variant),
|
||||
Annotatable::Item(item) => finder.visit_item(item),
|
||||
Annotatable::TraitItem(item) => finder.visit_assoc_item(item, visit::AssocCtxt::Trait),
|
||||
Annotatable::ImplItem(item) => finder.visit_assoc_item(item, visit::AssocCtxt::Impl),
|
||||
Annotatable::ForeignItem(item) => finder.visit_foreign_item(item),
|
||||
Annotatable::Stmt(stmt) => finder.visit_stmt(stmt),
|
||||
Annotatable::Expr(expr) => finder.visit_expr(expr),
|
||||
Annotatable::Arm(arm) => finder.visit_arm(arm),
|
||||
Annotatable::ExprField(field) => finder.visit_expr_field(field),
|
||||
Annotatable::PatField(field) => finder.visit_pat_field(field),
|
||||
Annotatable::GenericParam(param) => finder.visit_generic_param(param),
|
||||
Annotatable::Param(param) => finder.visit_param(param),
|
||||
Annotatable::FieldDef(field) => finder.visit_field_def(field),
|
||||
Annotatable::Variant(variant) => finder.visit_variant(variant),
|
||||
Annotatable::Crate(krate) => finder.visit_crate(krate),
|
||||
};
|
||||
finder.has_cfg_or_cfg_attr
|
||||
|
||||
@ -11,7 +11,7 @@ pub fn inject(krate: &mut ast::Crate, parse_sess: &ParseSess, attrs: &[String])
|
||||
for raw_attr in attrs {
|
||||
let mut parser = rustc_parse::new_parser_from_source_str(
|
||||
parse_sess,
|
||||
FileName::cli_crate_attr_source_code(&raw_attr),
|
||||
FileName::cli_crate_attr_source_code(raw_attr),
|
||||
raw_attr.clone(),
|
||||
);
|
||||
|
||||
@ -25,9 +25,7 @@ pub fn inject(krate: &mut ast::Crate, parse_sess: &ParseSess, attrs: &[String])
|
||||
};
|
||||
let end_span = parser.token.span;
|
||||
if parser.token != token::Eof {
|
||||
parse_sess
|
||||
.span_diagnostic
|
||||
.emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
|
||||
parse_sess.dcx.emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -159,7 +159,7 @@ pub fn expand_concat_bytes(
|
||||
accumulator.push(val);
|
||||
}
|
||||
Ok(ast::LitKind::ByteStr(ref bytes, _)) => {
|
||||
accumulator.extend_from_slice(&bytes);
|
||||
accumulator.extend_from_slice(bytes);
|
||||
}
|
||||
_ => {
|
||||
if !has_errors {
|
||||
|
||||
@ -35,7 +35,7 @@ impl MultiItemModifier for Expander {
|
||||
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
|
||||
validate_attr::check_builtin_meta_item(
|
||||
&sess.parse_sess,
|
||||
&meta_item,
|
||||
meta_item,
|
||||
ast::AttrStyle::Outer,
|
||||
sym::derive,
|
||||
template,
|
||||
@ -48,14 +48,14 @@ impl MultiItemModifier for Expander {
|
||||
NestedMetaItem::MetaItem(meta) => Some(meta),
|
||||
NestedMetaItem::Lit(lit) => {
|
||||
// Reject `#[derive("Debug")]`.
|
||||
report_unexpected_meta_item_lit(sess, &lit);
|
||||
report_unexpected_meta_item_lit(sess, lit);
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|meta| {
|
||||
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
|
||||
// paths.
|
||||
report_path_args(sess, &meta);
|
||||
report_path_args(sess, meta);
|
||||
meta.path.clone()
|
||||
})
|
||||
.map(|path| (path, dummy_annotatable(), None, self.0))
|
||||
|
||||
@ -188,7 +188,7 @@ fn cs_clone(
|
||||
}
|
||||
|
||||
let expr = match *vdata {
|
||||
VariantData::Struct(..) => {
|
||||
VariantData::Struct { .. } => {
|
||||
let fields = all_fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
|
||||
@ -136,7 +136,7 @@ fn cs_partial_cmp(
|
||||
&& let Some(last) = arms.last_mut()
|
||||
&& let PatKind::Wild = last.pat.kind
|
||||
{
|
||||
last.body = expr2;
|
||||
last.body = Some(expr2);
|
||||
expr1
|
||||
} else {
|
||||
let eq_arm = cx.arm(
|
||||
|
||||
@ -71,7 +71,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
|
||||
(false, 0)
|
||||
}
|
||||
ast::VariantData::Tuple(..) => (false, 1),
|
||||
ast::VariantData::Struct(..) => (true, 2),
|
||||
ast::VariantData::Struct { .. } => (true, 2),
|
||||
};
|
||||
|
||||
// The number of fields that can be handled without an array.
|
||||
@ -226,7 +226,7 @@ fn show_fieldless_enum(
|
||||
debug_assert!(fields.is_empty());
|
||||
cx.pat_tuple_struct(span, variant_path, ThinVec::new())
|
||||
}
|
||||
ast::VariantData::Struct(fields, _) => {
|
||||
ast::VariantData::Struct { fields, .. } => {
|
||||
debug_assert!(fields.is_empty());
|
||||
cx.pat_struct(span, variant_path, ThinVec::new())
|
||||
}
|
||||
|
||||
@ -127,18 +127,17 @@ fn extract_default_variant<'a>(
|
||||
[first, rest @ ..] => {
|
||||
let suggs = default_variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let spans = default_variants
|
||||
.filter_map(|variant| {
|
||||
let keep = attr::find_by_name(&variant.attrs, kw::Default)?.span;
|
||||
let spans: Vec<Span> = default_variants
|
||||
.iter()
|
||||
.filter_map(|v| {
|
||||
if v.span == variant.span {
|
||||
None
|
||||
} else {
|
||||
Some(attr::find_by_name(&v.attrs, kw::Default)?.span)
|
||||
}
|
||||
.flat_map(|v| {
|
||||
attr::filter_by_name(&v.attrs, kw::Default)
|
||||
.filter_map(|attr| (attr.span != keep).then_some(attr.span))
|
||||
})
|
||||
.collect();
|
||||
errors::MultipleDefaultsSugg { spans, ident: variant.ident }
|
||||
(!spans.is_empty())
|
||||
.then_some(errors::MultipleDefaultsSugg { spans, ident: variant.ident })
|
||||
})
|
||||
.collect();
|
||||
cx.emit_err(errors::MultipleDefaults {
|
||||
|
||||
@ -467,7 +467,7 @@ impl<'a> TraitDef<'a> {
|
||||
match item {
|
||||
Annotatable::Item(item) => {
|
||||
let is_packed = item.attrs.iter().any(|attr| {
|
||||
for r in attr::find_repr_attrs(&cx.sess, attr) {
|
||||
for r in attr::find_repr_attrs(cx.sess, attr) {
|
||||
if let attr::ReprPacked(_) = r {
|
||||
return true;
|
||||
}
|
||||
@ -478,7 +478,7 @@ impl<'a> TraitDef<'a> {
|
||||
let newitem = match &item.kind {
|
||||
ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def(
|
||||
cx,
|
||||
&struct_def,
|
||||
struct_def,
|
||||
item.ident,
|
||||
generics,
|
||||
from_scratch,
|
||||
@ -496,7 +496,7 @@ impl<'a> TraitDef<'a> {
|
||||
if self.supports_unions {
|
||||
self.expand_struct_def(
|
||||
cx,
|
||||
&struct_def,
|
||||
struct_def,
|
||||
item.ident,
|
||||
generics,
|
||||
from_scratch,
|
||||
@ -1485,7 +1485,7 @@ impl<'a> TraitDef<'a> {
|
||||
|
||||
let struct_path = struct_path.clone();
|
||||
match *struct_def {
|
||||
VariantData::Struct(..) => {
|
||||
VariantData::Struct { .. } => {
|
||||
let field_pats = pieces_iter
|
||||
.map(|(sp, ident, pat)| {
|
||||
if ident.is_none() {
|
||||
|
||||
@ -182,7 +182,7 @@ impl Bounds {
|
||||
let params = self
|
||||
.bounds
|
||||
.iter()
|
||||
.map(|&(name, ref bounds)| mk_ty_param(cx, span, name, &bounds, self_ty, self_generics))
|
||||
.map(|&(name, ref bounds)| mk_ty_param(cx, span, name, bounds, self_ty, self_generics))
|
||||
.collect();
|
||||
|
||||
Generics {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user