Merge ref 'b56aaec52b' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh.
Upstream ref: b56aaec52b
Filtered ref: 12f5e3255df658296af9fc953d8c1ab79ba91ea3
This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
commit
8ff5894541
1397 changed files with 14631 additions and 12110 deletions
23
.github/workflows/spellcheck.yml
vendored
23
.github/workflows/spellcheck.yml
vendored
|
|
@ -1,23 +0,0 @@
|
|||
# This workflow runs spellcheck job
|
||||
|
||||
name: Spellcheck
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "**"
|
||||
|
||||
jobs:
|
||||
spellcheck:
|
||||
name: run spellchecker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: check typos
|
||||
# sync version with src/tools/tidy/src/ext_tool_checks.rs in spellcheck_runner
|
||||
uses: crate-ci/typos@v1.34.0
|
||||
with:
|
||||
# sync target files with src/tools/tidy/src/ext_tool_checks.rs in check_impl
|
||||
files: ./compiler ./library ./src/bootstrap ./src/librustdoc
|
||||
config: ./typos.toml
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -85,8 +85,6 @@ __pycache__/
|
|||
|
||||
## Node
|
||||
node_modules
|
||||
package-lock.json
|
||||
package.json
|
||||
/src/doc/rustc-dev-guide/mermaid.min.js
|
||||
|
||||
## Rustdoc GUI tests
|
||||
|
|
|
|||
13
Cargo.lock
13
Cargo.lock
|
|
@ -4563,7 +4563,10 @@ dependencies = [
|
|||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_span",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -4955,6 +4958,16 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_path_to_error"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.9"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ path = [
|
|||
"rust-bors.toml",
|
||||
"triagebot.toml",
|
||||
"typos.toml",
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"x",
|
||||
"x.ps1",
|
||||
"x.py",
|
||||
|
|
|
|||
|
|
@ -313,7 +313,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
scalar_valid_range: (Bound<u128>, Bound<u128>),
|
||||
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
|
||||
discriminants: impl Iterator<Item = (VariantIdx, i128)>,
|
||||
dont_niche_optimize_enum: bool,
|
||||
always_sized: bool,
|
||||
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
|
||||
let (present_first, present_second) = {
|
||||
|
|
@ -352,13 +351,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
// structs. (We have also handled univariant enums
|
||||
// that allow representation optimization.)
|
||||
assert!(is_enum);
|
||||
self.layout_of_enum(
|
||||
repr,
|
||||
variants,
|
||||
discr_range_of_repr,
|
||||
discriminants,
|
||||
dont_niche_optimize_enum,
|
||||
)
|
||||
self.layout_of_enum(repr, variants, discr_range_of_repr, discriminants)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -599,7 +592,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
|
||||
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
|
||||
discriminants: impl Iterator<Item = (VariantIdx, i128)>,
|
||||
dont_niche_optimize_enum: bool,
|
||||
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
|
||||
// Until we've decided whether to use the tagged or
|
||||
// niche filling LayoutData, we don't want to intern the
|
||||
|
|
@ -618,7 +610,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
}
|
||||
|
||||
let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> {
|
||||
if dont_niche_optimize_enum {
|
||||
if repr.inhibit_enum_layout_opt() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1376,6 +1376,28 @@ impl WrappingRange {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if all the values in `other` are contained in this range,
|
||||
/// when the values are considered as having width `size`.
|
||||
#[inline(always)]
|
||||
pub fn contains_range(&self, other: Self, size: Size) -> bool {
|
||||
if self.is_full_for(size) {
|
||||
true
|
||||
} else {
|
||||
let trunc = |x| size.truncate(x);
|
||||
|
||||
let delta = self.start;
|
||||
let max = trunc(self.end.wrapping_sub(delta));
|
||||
|
||||
let other_start = trunc(other.start.wrapping_sub(delta));
|
||||
let other_end = trunc(other.end.wrapping_sub(delta));
|
||||
|
||||
// Having shifted both input ranges by `delta`, now we only need to check
|
||||
// whether `0..=max` contains `other_start..=other_end`, which can only
|
||||
// happen if the other doesn't wrap since `self` isn't everything.
|
||||
(other_start <= other_end) && (other_end <= max)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `self` with replaced `start`
|
||||
#[inline(always)]
|
||||
fn with_start(mut self, start: u128) -> Self {
|
||||
|
|
|
|||
|
|
@ -5,3 +5,66 @@ fn align_constants() {
|
|||
assert_eq!(Align::ONE, Align::from_bytes(1).unwrap());
|
||||
assert_eq!(Align::EIGHT, Align::from_bytes(8).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapping_range_contains_range() {
|
||||
let size16 = Size::from_bytes(16);
|
||||
|
||||
let a = WrappingRange { start: 10, end: 20 };
|
||||
assert!(a.contains_range(a, size16));
|
||||
assert!(a.contains_range(WrappingRange { start: 11, end: 19 }, size16));
|
||||
assert!(a.contains_range(WrappingRange { start: 10, end: 10 }, size16));
|
||||
assert!(a.contains_range(WrappingRange { start: 20, end: 20 }, size16));
|
||||
assert!(!a.contains_range(WrappingRange { start: 10, end: 21 }, size16));
|
||||
assert!(!a.contains_range(WrappingRange { start: 9, end: 20 }, size16));
|
||||
assert!(!a.contains_range(WrappingRange { start: 4, end: 6 }, size16));
|
||||
assert!(!a.contains_range(WrappingRange { start: 24, end: 26 }, size16));
|
||||
|
||||
assert!(!a.contains_range(WrappingRange { start: 16, end: 14 }, size16));
|
||||
|
||||
let b = WrappingRange { start: 20, end: 10 };
|
||||
assert!(b.contains_range(b, size16));
|
||||
assert!(b.contains_range(WrappingRange { start: 20, end: 20 }, size16));
|
||||
assert!(b.contains_range(WrappingRange { start: 10, end: 10 }, size16));
|
||||
assert!(b.contains_range(WrappingRange { start: 0, end: 10 }, size16));
|
||||
assert!(b.contains_range(WrappingRange { start: 20, end: 30 }, size16));
|
||||
assert!(b.contains_range(WrappingRange { start: 20, end: 9 }, size16));
|
||||
assert!(b.contains_range(WrappingRange { start: 21, end: 10 }, size16));
|
||||
assert!(b.contains_range(WrappingRange { start: 999, end: 9999 }, size16));
|
||||
assert!(b.contains_range(WrappingRange { start: 999, end: 9 }, size16));
|
||||
assert!(!b.contains_range(WrappingRange { start: 19, end: 19 }, size16));
|
||||
assert!(!b.contains_range(WrappingRange { start: 11, end: 11 }, size16));
|
||||
assert!(!b.contains_range(WrappingRange { start: 19, end: 11 }, size16));
|
||||
assert!(!b.contains_range(WrappingRange { start: 11, end: 19 }, size16));
|
||||
|
||||
let f = WrappingRange { start: 0, end: u128::MAX };
|
||||
assert!(f.contains_range(WrappingRange { start: 10, end: 20 }, size16));
|
||||
assert!(f.contains_range(WrappingRange { start: 20, end: 10 }, size16));
|
||||
|
||||
let g = WrappingRange { start: 2, end: 1 };
|
||||
assert!(g.contains_range(WrappingRange { start: 10, end: 20 }, size16));
|
||||
assert!(g.contains_range(WrappingRange { start: 20, end: 10 }, size16));
|
||||
|
||||
let size1 = Size::from_bytes(1);
|
||||
let u8r = WrappingRange { start: 0, end: 255 };
|
||||
let i8r = WrappingRange { start: 128, end: 127 };
|
||||
assert!(u8r.contains_range(i8r, size1));
|
||||
assert!(i8r.contains_range(u8r, size1));
|
||||
assert!(!u8r.contains_range(i8r, size16));
|
||||
assert!(i8r.contains_range(u8r, size16));
|
||||
|
||||
let boolr = WrappingRange { start: 0, end: 1 };
|
||||
assert!(u8r.contains_range(boolr, size1));
|
||||
assert!(i8r.contains_range(boolr, size1));
|
||||
assert!(!boolr.contains_range(u8r, size1));
|
||||
assert!(!boolr.contains_range(i8r, size1));
|
||||
|
||||
let cmpr = WrappingRange { start: 255, end: 1 };
|
||||
assert!(u8r.contains_range(cmpr, size1));
|
||||
assert!(i8r.contains_range(cmpr, size1));
|
||||
assert!(!cmpr.contains_range(u8r, size1));
|
||||
assert!(!cmpr.contains_range(i8r, size1));
|
||||
|
||||
assert!(!boolr.contains_range(cmpr, size1));
|
||||
assert!(cmpr.contains_range(boolr, size1));
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
use rustc_macros::{Decodable, Encodable, Walkable};
|
||||
use rustc_span::{Ident, Span, Symbol};
|
||||
|
||||
use crate::Expr;
|
||||
|
|
@ -41,7 +41,7 @@ use crate::token::LitKind;
|
|||
/// Basically the "AST" for a complete `format_args!()`.
|
||||
///
|
||||
/// E.g., `format_args!("hello {name}");`.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub struct FormatArgs {
|
||||
pub span: Span,
|
||||
pub template: Vec<FormatArgsPiece>,
|
||||
|
|
@ -63,7 +63,7 @@ pub struct FormatArgs {
|
|||
/// A piece of a format template string.
|
||||
///
|
||||
/// E.g. "hello" or "{name}".
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub enum FormatArgsPiece {
|
||||
Literal(Symbol),
|
||||
Placeholder(FormatPlaceholder),
|
||||
|
|
@ -73,7 +73,7 @@ pub enum FormatArgsPiece {
|
|||
///
|
||||
/// E.g. `1, 2, name="ferris", n=3`,
|
||||
/// but also implicit captured arguments like `x` in `format_args!("{x}")`.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub struct FormatArguments {
|
||||
arguments: Vec<FormatArgument>,
|
||||
num_unnamed_args: usize,
|
||||
|
|
@ -144,13 +144,13 @@ impl FormatArguments {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub struct FormatArgument {
|
||||
pub kind: FormatArgumentKind,
|
||||
pub expr: P<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub enum FormatArgumentKind {
|
||||
/// `format_args(…, arg)`
|
||||
Normal,
|
||||
|
|
@ -170,24 +170,28 @@ impl FormatArgumentKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
|
||||
pub struct FormatPlaceholder {
|
||||
/// Index into [`FormatArgs::arguments`].
|
||||
pub argument: FormatArgPosition,
|
||||
/// The span inside the format string for the full `{…}` placeholder.
|
||||
pub span: Option<Span>,
|
||||
/// `{}`, `{:?}`, or `{:x}`, etc.
|
||||
#[visitable(ignore)]
|
||||
pub format_trait: FormatTrait,
|
||||
/// `{}` or `{:.5}` or `{:-^20}`, etc.
|
||||
#[visitable(ignore)]
|
||||
pub format_options: FormatOptions,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
|
||||
pub struct FormatArgPosition {
|
||||
/// Which argument this position refers to (Ok),
|
||||
/// or would've referred to if it existed (Err).
|
||||
#[visitable(ignore)]
|
||||
pub index: Result<usize, usize>,
|
||||
/// What kind of position this is. See [`FormatArgPositionKind`].
|
||||
#[visitable(ignore)]
|
||||
pub kind: FormatArgPositionKind,
|
||||
/// The span of the name or number.
|
||||
pub span: Option<Span>,
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ use std::panic;
|
|||
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Ident, Span};
|
||||
use rustc_span::{Ident, Span, Symbol};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::ptr::P;
|
||||
use crate::tokenstream::*;
|
||||
use crate::visit::{AssocCtxt, BoundKind, FnCtxt, VisitorResult, try_visit, visit_opt, walk_list};
|
||||
use crate::visit::{AssocCtxt, BoundKind, FnCtxt, LifetimeCtxt, VisitorResult, try_visit};
|
||||
|
||||
mod sealed {
|
||||
use rustc_ast_ir::visit::VisitorResult;
|
||||
|
|
@ -36,11 +36,249 @@ mod sealed {
|
|||
|
||||
use sealed::MutVisitorResult;
|
||||
|
||||
pub(crate) trait MutVisitable<V: MutVisitor> {
|
||||
type Extra: Copy;
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra);
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T: ?Sized> MutVisitable<V> for P<T>
|
||||
where
|
||||
T: MutVisitable<V>,
|
||||
{
|
||||
type Extra = T::Extra;
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
(**self).visit_mut(visitor, extra)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T> MutVisitable<V> for Option<T>
|
||||
where
|
||||
T: MutVisitable<V>,
|
||||
{
|
||||
type Extra = T::Extra;
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
if let Some(this) = self {
|
||||
this.visit_mut(visitor, extra)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T> MutVisitable<V> for Spanned<T>
|
||||
where
|
||||
T: MutVisitable<V>,
|
||||
{
|
||||
type Extra = T::Extra;
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
let Spanned { span, node } = self;
|
||||
span.visit_mut(visitor, ());
|
||||
node.visit_mut(visitor, extra);
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T> MutVisitable<V> for [T]
|
||||
where
|
||||
T: MutVisitable<V>,
|
||||
{
|
||||
type Extra = T::Extra;
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
for item in self {
|
||||
item.visit_mut(visitor, extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T> MutVisitable<V> for Vec<T>
|
||||
where
|
||||
T: MutVisitable<V>,
|
||||
{
|
||||
type Extra = T::Extra;
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
for item in self {
|
||||
item.visit_mut(visitor, extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T> MutVisitable<V> for (T,)
|
||||
where
|
||||
T: MutVisitable<V>,
|
||||
{
|
||||
type Extra = T::Extra;
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
self.0.visit_mut(visitor, extra);
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T1, T2> MutVisitable<V> for (T1, T2)
|
||||
where
|
||||
T1: MutVisitable<V, Extra = ()>,
|
||||
T2: MutVisitable<V, Extra = ()>,
|
||||
{
|
||||
type Extra = ();
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
self.0.visit_mut(visitor, extra);
|
||||
self.1.visit_mut(visitor, extra);
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T1, T2, T3> MutVisitable<V> for (T1, T2, T3)
|
||||
where
|
||||
T1: MutVisitable<V, Extra = ()>,
|
||||
T2: MutVisitable<V, Extra = ()>,
|
||||
T3: MutVisitable<V, Extra = ()>,
|
||||
{
|
||||
type Extra = ();
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
self.0.visit_mut(visitor, extra);
|
||||
self.1.visit_mut(visitor, extra);
|
||||
self.2.visit_mut(visitor, extra);
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: MutVisitor, T1, T2, T3, T4> MutVisitable<V> for (T1, T2, T3, T4)
|
||||
where
|
||||
T1: MutVisitable<V, Extra = ()>,
|
||||
T2: MutVisitable<V, Extra = ()>,
|
||||
T3: MutVisitable<V, Extra = ()>,
|
||||
T4: MutVisitable<V, Extra = ()>,
|
||||
{
|
||||
type Extra = ();
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
self.0.visit_mut(visitor, extra);
|
||||
self.1.visit_mut(visitor, extra);
|
||||
self.2.visit_mut(visitor, extra);
|
||||
self.3.visit_mut(visitor, extra);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MutWalkable<V: MutVisitor> {
|
||||
fn walk_mut(&mut self, visitor: &mut V);
|
||||
}
|
||||
|
||||
macro_rules! visit_visitable {
|
||||
(mut $visitor:expr, $($expr:expr),* $(,)?) => {{
|
||||
$(MutVisitable::visit_mut($expr, $visitor, ());)*
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! visit_visitable_with {
|
||||
(mut $visitor:expr, $expr:expr, $extra:expr $(,)?) => {
|
||||
MutVisitable::visit_mut($expr, $visitor, $extra)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! walk_walkable {
|
||||
($visitor:expr, $expr:expr, mut) => {
|
||||
MutWalkable::walk_mut($expr, $visitor)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_visitable {
|
||||
(|&mut $self:ident: $self_ty:ty,
|
||||
$vis:ident: &mut $vis_ty:ident,
|
||||
$extra:ident: $extra_ty:ty| $block:block) => {
|
||||
#[allow(unused_parens, non_local_definitions)]
|
||||
impl<$vis_ty: MutVisitor> MutVisitable<$vis_ty> for $self_ty {
|
||||
type Extra = $extra_ty;
|
||||
fn visit_mut(&mut $self, $vis: &mut $vis_ty, $extra: Self::Extra) -> V::Result {
|
||||
$block
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_walkable {
|
||||
($(<$K:ident: $Kb:ident>)? |&mut $self:ident: $self_ty:ty,
|
||||
$vis:ident: &mut $vis_ty:ident| $block:block) => {
|
||||
#[allow(unused_parens, non_local_definitions)]
|
||||
impl<$($K: $Kb,)? $vis_ty: MutVisitor> MutWalkable<$vis_ty> for $self_ty {
|
||||
fn walk_mut(&mut $self, $vis: &mut $vis_ty) -> V::Result {
|
||||
$block
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_visitable_noop {
|
||||
(<mut> $($ty:ty,)*) => {
|
||||
$(
|
||||
impl_visitable!(|&mut self: $ty, _vis: &mut V, _extra: ()| {});
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_visitable_list {
|
||||
(<mut> $($ty:ty,)*) => {
|
||||
$(impl<V: MutVisitor, T> MutVisitable<V> for $ty
|
||||
where
|
||||
for<'a> &'a mut $ty: IntoIterator<Item = &'a mut T>,
|
||||
T: MutVisitable<V>,
|
||||
{
|
||||
type Extra = <T as MutVisitable<V>>::Extra;
|
||||
|
||||
#[inline]
|
||||
fn visit_mut(&mut self, visitor: &mut V, extra: Self::Extra) {
|
||||
for i in self {
|
||||
i.visit_mut(visitor, extra);
|
||||
}
|
||||
}
|
||||
})*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_visitable_direct {
|
||||
(<mut> $($ty:ty,)*) => {
|
||||
$(impl_visitable!(
|
||||
|&mut self: $ty, visitor: &mut V, _extra: ()| {
|
||||
MutWalkable::walk_mut(self, visitor)
|
||||
}
|
||||
);)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_visitable_calling_walkable {
|
||||
(<mut>
|
||||
$( fn $method:ident($ty:ty $(, $extra_name:ident: $extra_ty:ty)?); )*
|
||||
) => {
|
||||
$(fn $method(&mut self, node: &mut $ty $(, $extra_name:$extra_ty)?) {
|
||||
impl_visitable!(|&mut self: $ty, visitor: &mut V, extra: ($($extra_ty)?)| {
|
||||
let ($($extra_name)?) = extra;
|
||||
visitor.$method(self $(, $extra_name)?);
|
||||
});
|
||||
walk_walkable!(self, node, mut)
|
||||
})*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_named_walk {
|
||||
((mut) $Visitor:ident
|
||||
$( pub fn $method:ident($ty:ty); )*
|
||||
) => {
|
||||
$(pub fn $method<V: $Visitor>(visitor: &mut V, node: &mut $ty) {
|
||||
walk_walkable!(visitor, node, mut)
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
super::common_visitor_and_walkers!((mut) MutVisitor);
|
||||
|
||||
macro_rules! generate_flat_map_visitor_fns {
|
||||
($($name:ident, $Ty:ty, $flat_map_fn:ident$(, $param:ident: $ParamTy:ty)*;)+) => {
|
||||
$(
|
||||
#[allow(unused_parens)]
|
||||
impl<V: MutVisitor> MutVisitable<V> for ThinVec<$Ty> {
|
||||
type Extra = ($($ParamTy),*);
|
||||
|
||||
#[inline]
|
||||
fn visit_mut(
|
||||
&mut self,
|
||||
visitor: &mut V,
|
||||
($($param),*): Self::Extra,
|
||||
) -> V::Result {
|
||||
$name(visitor, self $(, $param)*)
|
||||
}
|
||||
}
|
||||
|
||||
fn $name<V: MutVisitor>(
|
||||
vis: &mut V,
|
||||
values: &mut ThinVec<$Ty>,
|
||||
|
|
@ -78,15 +316,6 @@ pub fn walk_flat_map_pat_field<T: MutVisitor>(
|
|||
smallvec![fp]
|
||||
}
|
||||
|
||||
fn visit_nested_use_tree<V: MutVisitor>(
|
||||
vis: &mut V,
|
||||
nested_tree: &mut UseTree,
|
||||
nested_id: &mut NodeId,
|
||||
) {
|
||||
vis.visit_id(nested_id);
|
||||
vis.visit_use_tree(nested_tree);
|
||||
}
|
||||
|
||||
macro_rules! generate_walk_flat_map_fns {
|
||||
($($fn_name:ident($Ty:ty$(,$extra_name:ident: $ExtraTy:ty)*) => $visit_fn_name:ident;)+) => {$(
|
||||
pub fn $fn_name<V: MutVisitor>(vis: &mut V, mut value: $Ty$(,$extra_name: $ExtraTy)*) -> SmallVec<[$Ty; 1]> {
|
||||
|
|
@ -109,14 +338,6 @@ generate_walk_flat_map_fns! {
|
|||
walk_flat_map_assoc_item(P<AssocItem>, ctxt: AssocCtxt) => visit_assoc_item;
|
||||
}
|
||||
|
||||
fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWhereClauses) {
|
||||
let TyAliasWhereClauses { before, after, split: _ } = tawcs;
|
||||
let TyAliasWhereClause { has_where_token: _, span: span_before } = before;
|
||||
let TyAliasWhereClause { has_where_token: _, span: span_after } = after;
|
||||
vis.visit_span(span_before);
|
||||
vis.visit_span(span_after);
|
||||
}
|
||||
|
||||
pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Option<P<Expr>> {
|
||||
vis.visit_expr(&mut e);
|
||||
Some(e)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use std::{cmp, fmt, iter, mem};
|
|||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable};
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
|
@ -977,7 +977,7 @@ impl TokenCursor {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
|
||||
pub struct DelimSpan {
|
||||
pub open: Span,
|
||||
pub close: Span,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -157,6 +157,19 @@ pub enum UsedBy {
|
|||
Linker,
|
||||
}
|
||||
|
||||
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum MacroUseArgs {
|
||||
UseAll,
|
||||
UseSpecific(ThinVec<Ident>),
|
||||
}
|
||||
|
||||
impl Default for MacroUseArgs {
|
||||
fn default() -> Self {
|
||||
Self::UseSpecific(ThinVec::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
|
||||
pub struct StrippedCfgItem<ModId = DefId> {
|
||||
pub parent_module: ModId,
|
||||
|
|
@ -351,9 +364,15 @@ pub enum AttributeKind {
|
|||
/// Represents `#[loop_match]`.
|
||||
LoopMatch(Span),
|
||||
|
||||
/// Represents `#[macro_escape]`.
|
||||
MacroEscape(Span),
|
||||
|
||||
/// Represents `#[rustc_macro_transparency]`.
|
||||
MacroTransparency(Transparency),
|
||||
|
||||
/// Represents `#[macro_use]`.
|
||||
MacroUse { span: Span, arguments: MacroUseArgs },
|
||||
|
||||
/// Represents `#[marker]`.
|
||||
Marker(Span),
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ impl AttributeKind {
|
|||
LinkOrdinal { .. } => No,
|
||||
LinkSection { .. } => Yes, // Needed for rustdoc
|
||||
LoopMatch(..) => No,
|
||||
MacroEscape(..) => No,
|
||||
MacroTransparency(..) => Yes,
|
||||
MacroUse { .. } => No,
|
||||
Marker(..) => No,
|
||||
MayDangle(..) => No,
|
||||
MustUse { .. } => Yes,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use rustc_ast::token::CommentKind;
|
|||
use rustc_ast::{AttrStyle, IntTy, UintTy};
|
||||
use rustc_ast_pretty::pp::Printer;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
|
||||
pub use stability::*;
|
||||
use thin_vec::ThinVec;
|
||||
pub use version::*;
|
||||
|
|
@ -172,7 +172,7 @@ macro_rules! print_tup {
|
|||
print_tup!(A B C D E F G H);
|
||||
print_skip!(Span, (), ErrorGuaranteed);
|
||||
print_disp!(u16, bool, NonZero<u32>);
|
||||
print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
|
||||
print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
|
||||
|
||||
/// Finds attributes in sequences of attributes by pattern matching.
|
||||
///
|
||||
|
|
|
|||
115
compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
Normal file
115
compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
use rustc_attr_data_structures::{AttributeKind, MacroUseArgs};
|
||||
use rustc_errors::DiagArgValue;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub(crate) struct MacroEscapeParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
|
||||
const PATH: &[Symbol] = &[sym::macro_escape];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape;
|
||||
}
|
||||
|
||||
/// `#[macro_use]` attributes can either:
|
||||
/// - Use all macros from a crate, if provided without arguments
|
||||
/// - Use specific macros from a crate, if provided with arguments `#[macro_use(macro1, macro2)]`
|
||||
/// A warning should be provided if an use all is combined with specific uses, or if multiple use-alls are used.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct MacroUseParser {
|
||||
state: MacroUseArgs,
|
||||
|
||||
/// Spans of all `#[macro_use]` arguments with arguments, used for linting
|
||||
uses_attr_spans: ThinVec<Span>,
|
||||
/// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute
|
||||
/// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments
|
||||
first_span: Option<Span>,
|
||||
}
|
||||
|
||||
const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ...");
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for MacroUseParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::macro_use],
|
||||
MACRO_USE_TEMPLATE,
|
||||
|group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| {
|
||||
let span = cx.attr_span;
|
||||
group.first_span.get_or_insert(span);
|
||||
match args {
|
||||
ArgParser::NoArgs => {
|
||||
match group.state {
|
||||
MacroUseArgs::UseAll => {
|
||||
let first_span = group.first_span.expect(
|
||||
"State is UseAll is some so this is not the first attribute",
|
||||
);
|
||||
// Since there is a `#[macro_use]` import already, give a warning
|
||||
cx.warn_unused_duplicate(first_span, span);
|
||||
}
|
||||
MacroUseArgs::UseSpecific(_) => {
|
||||
group.state = MacroUseArgs::UseAll;
|
||||
group.first_span = Some(span);
|
||||
// If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported
|
||||
for specific_use in group.uses_attr_spans.drain(..) {
|
||||
cx.warn_unused_duplicate(span, specific_use);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::List(list) => {
|
||||
if list.is_empty() {
|
||||
cx.warn_empty_attribute(list.span);
|
||||
return;
|
||||
}
|
||||
|
||||
match &mut group.state {
|
||||
MacroUseArgs::UseAll => {
|
||||
let first_span = group.first_span.expect(
|
||||
"State is UseAll is some so this is not the first attribute",
|
||||
);
|
||||
cx.warn_unused_duplicate(first_span, span);
|
||||
}
|
||||
MacroUseArgs::UseSpecific(arguments) => {
|
||||
// Store here so if we encounter a `UseAll` later we can still lint this attribute
|
||||
group.uses_attr_spans.push(cx.attr_span);
|
||||
|
||||
for item in list.mixed() {
|
||||
let Some(item) = item.meta_item() else {
|
||||
cx.expected_identifier(item.span());
|
||||
continue;
|
||||
};
|
||||
if let Err(err_span) = item.args().no_args() {
|
||||
cx.expected_no_args(err_span);
|
||||
continue;
|
||||
}
|
||||
let Some(item) = item.path().word() else {
|
||||
cx.expected_identifier(item.span());
|
||||
continue;
|
||||
};
|
||||
arguments.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
|
||||
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
|
||||
),
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
)];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ pub(crate) mod inline;
|
|||
pub(crate) mod link_attrs;
|
||||
pub(crate) mod lint_helpers;
|
||||
pub(crate) mod loop_match;
|
||||
pub(crate) mod macro_attrs;
|
||||
pub(crate) mod must_use;
|
||||
pub(crate) mod no_implicit_prelude;
|
||||
pub(crate) mod non_exhaustive;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
|
|||
ArgParser::List(_) => {
|
||||
let suggestions =
|
||||
<Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
|
||||
cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput {
|
||||
cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
|
||||
num_suggestions: suggestions.len(),
|
||||
suggestions: DiagArgValue::StrListSepByAnd(
|
||||
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
|
||||
|
|
|
|||
|
|
@ -74,8 +74,15 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
|
|||
template!(NameValueStr: "deprecation message"),
|
||||
|this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
this.allowed_through_unstable_modules =
|
||||
args.name_value().and_then(|i| i.value_as_str())
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return;
|
||||
};
|
||||
let Some(value_str) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return;
|
||||
};
|
||||
this.allowed_through_unstable_modules = Some(value_str);
|
||||
},
|
||||
),
|
||||
];
|
||||
|
|
@ -247,7 +254,12 @@ pub(crate) fn parse_stability<S: Stage>(
|
|||
let mut feature = None;
|
||||
let mut since = None;
|
||||
|
||||
for param in args.list()?.mixed() {
|
||||
let ArgParser::List(list) = args else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
|
||||
for param in list.mixed() {
|
||||
let param_span = param.span();
|
||||
let Some(param) = param.meta_item() else {
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
|
|
@ -322,7 +334,13 @@ pub(crate) fn parse_unstability<S: Stage>(
|
|||
let mut is_soft = false;
|
||||
let mut implied_by = None;
|
||||
let mut old_name = None;
|
||||
for param in args.list()?.mixed() {
|
||||
|
||||
let ArgParser::List(list) = args else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
|
||||
for param in list.mixed() {
|
||||
let Some(param) = param.meta_item() else {
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: param.span(),
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ use crate::attributes::lint_helpers::{
|
|||
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
|
||||
};
|
||||
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
|
||||
use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
|
||||
use crate::attributes::must_use::MustUseParser;
|
||||
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
|
||||
use crate::attributes::non_exhaustive::NonExhaustiveParser;
|
||||
|
|
@ -126,6 +127,7 @@ attribute_parsers!(
|
|||
BodyStabilityParser,
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
MacroUseParser,
|
||||
NakedParser,
|
||||
StabilityParser,
|
||||
UsedParser,
|
||||
|
|
@ -174,6 +176,7 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<FfiPureParser>>,
|
||||
Single<WithoutArgs<FundamentalParser>>,
|
||||
Single<WithoutArgs<LoopMatchParser>>,
|
||||
Single<WithoutArgs<MacroEscapeParser>>,
|
||||
Single<WithoutArgs<MarkerParser>>,
|
||||
Single<WithoutArgs<MayDangleParser>>,
|
||||
Single<WithoutArgs<NoImplicitPreludeParser>>,
|
||||
|
|
@ -386,6 +389,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
})
|
||||
}
|
||||
|
||||
/// emit an error that a `name` was expected here
|
||||
pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
|
||||
self.emit_err(AttributeParseError {
|
||||
span,
|
||||
attr_span: self.attr_span,
|
||||
template: self.template.clone(),
|
||||
attribute: self.attr_path.clone(),
|
||||
reason: AttributeParseErrorReason::ExpectedIdentifier,
|
||||
})
|
||||
}
|
||||
|
||||
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
|
||||
/// a nicer error message talking about the specific name that was found lacking a value.
|
||||
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
|
||||
|
|
|
|||
|
|
@ -438,7 +438,7 @@ pub(crate) struct IllFormedAttributeInput {
|
|||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_ill_formed_attribute_input)]
|
||||
pub(crate) struct MustUseIllFormedAttributeInput {
|
||||
pub(crate) struct IllFormedAttributeInputLint {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub num_suggestions: usize,
|
||||
|
|
@ -549,6 +549,7 @@ pub(crate) enum AttributeParseErrorReason {
|
|||
/// Should we tell the user to write a list when they didn't?
|
||||
list: bool,
|
||||
},
|
||||
ExpectedIdentifier,
|
||||
}
|
||||
|
||||
pub(crate) struct AttributeParseError {
|
||||
|
|
@ -600,11 +601,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
|||
diag.code(E0538);
|
||||
}
|
||||
AttributeParseErrorReason::UnexpectedLiteral => {
|
||||
diag.span_label(self.span, format!("didn't expect a literal here"));
|
||||
diag.span_label(self.span, "didn't expect a literal here");
|
||||
diag.code(E0565);
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedNoArgs => {
|
||||
diag.span_label(self.span, format!("didn't expect any arguments here"));
|
||||
diag.span_label(self.span, "didn't expect any arguments here");
|
||||
diag.code(E0565);
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedNameValue(None) => {
|
||||
|
|
@ -684,6 +685,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
|
|||
}
|
||||
}
|
||||
}
|
||||
AttributeParseErrorReason::ExpectedIdentifier => {
|
||||
diag.span_label(self.span, "expected a valid identifier here");
|
||||
}
|
||||
}
|
||||
|
||||
let suggestions = self.template.suggestions(false, &name);
|
||||
|
|
|
|||
|
|
@ -115,10 +115,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
fn append_to_grouped_errors(
|
||||
&self,
|
||||
grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
|
||||
error: MoveError<'tcx>,
|
||||
MoveError { place: original_path, location, kind }: MoveError<'tcx>,
|
||||
) {
|
||||
let MoveError { place: original_path, location, kind } = error;
|
||||
|
||||
// Note: that the only time we assign a place isn't a temporary
|
||||
// to a user variable is when initializing it.
|
||||
// If that ever stops being the case, then the ever initialized
|
||||
|
|
@ -251,62 +249,56 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
|
||||
fn report(&mut self, error: GroupedMoveError<'tcx>) {
|
||||
let (mut err, err_span) = {
|
||||
let (span, use_spans, original_path, kind) = match error {
|
||||
GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
|
||||
| GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
|
||||
(span, None, original_path, kind)
|
||||
}
|
||||
GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
|
||||
(use_spans.args_or_use(), Some(use_spans), original_path, kind)
|
||||
}
|
||||
};
|
||||
debug!(
|
||||
"report: original_path={:?} span={:?}, kind={:?} \
|
||||
original_path.is_upvar_field_projection={:?}",
|
||||
original_path,
|
||||
span,
|
||||
kind,
|
||||
self.is_upvar_field_projection(original_path.as_ref())
|
||||
);
|
||||
if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
|
||||
// If the type may implement Copy, skip the error.
|
||||
// It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check
|
||||
self.dcx().span_delayed_bug(
|
||||
span,
|
||||
"Type may implement copy, but there is no other error.",
|
||||
);
|
||||
return;
|
||||
let (span, use_spans, original_path, kind) = match error {
|
||||
GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
|
||||
| GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
|
||||
(span, None, original_path, kind)
|
||||
}
|
||||
GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
|
||||
(use_spans.args_or_use(), Some(use_spans), original_path, kind)
|
||||
}
|
||||
};
|
||||
debug!(
|
||||
"report: original_path={:?} span={:?}, kind={:?} \
|
||||
original_path.is_upvar_field_projection={:?}",
|
||||
original_path,
|
||||
span,
|
||||
kind,
|
||||
self.is_upvar_field_projection(original_path.as_ref())
|
||||
);
|
||||
if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
|
||||
// If the type may implement Copy, skip the error.
|
||||
// It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check
|
||||
self.dcx()
|
||||
.span_delayed_bug(span, "Type may implement copy, but there is no other error.");
|
||||
return;
|
||||
}
|
||||
let mut err = match kind {
|
||||
&IllegalMoveOriginKind::BorrowedContent { target_place } => self
|
||||
.report_cannot_move_from_borrowed_content(
|
||||
original_path,
|
||||
target_place,
|
||||
span,
|
||||
use_spans,
|
||||
),
|
||||
&IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
|
||||
self.cannot_move_out_of_interior_of_drop(span, ty)
|
||||
}
|
||||
&IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
|
||||
self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
|
||||
}
|
||||
(
|
||||
match kind {
|
||||
&IllegalMoveOriginKind::BorrowedContent { target_place } => self
|
||||
.report_cannot_move_from_borrowed_content(
|
||||
original_path,
|
||||
target_place,
|
||||
span,
|
||||
use_spans,
|
||||
),
|
||||
&IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
|
||||
self.cannot_move_out_of_interior_of_drop(span, ty)
|
||||
}
|
||||
&IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
|
||||
self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
|
||||
}
|
||||
},
|
||||
span,
|
||||
)
|
||||
};
|
||||
|
||||
self.add_move_hints(error, &mut err, err_span);
|
||||
self.add_move_hints(error, &mut err, span);
|
||||
self.buffer_error(err);
|
||||
}
|
||||
|
||||
fn has_ambiguous_copy(&mut self, ty: Ty<'tcx>) -> bool {
|
||||
let Some(copy_trait_def) = self.infcx.tcx.lang_items().copy_trait() else { return false };
|
||||
// This is only going to be ambiguous if there are incoherent impls, because otherwise
|
||||
// ambiguity should never happen in MIR.
|
||||
self.infcx.type_implements_trait(copy_trait_def, [ty], self.infcx.param_env).may_apply()
|
||||
let Some(copy_def_id) = self.infcx.tcx.lang_items().copy_trait() else { return false };
|
||||
|
||||
// Avoid bogus move errors because of an incoherent `Copy` impl.
|
||||
self.infcx.type_implements_trait(copy_def_id, [ty], self.infcx.param_env).may_apply()
|
||||
&& self.infcx.tcx.coherent_trait(copy_def_id).is_err()
|
||||
}
|
||||
|
||||
fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'infcx> {
|
||||
|
|
@ -482,7 +474,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
self.cannot_move_out_of_interior_noncopy(span, ty, None)
|
||||
}
|
||||
ty::Closure(def_id, closure_args)
|
||||
if def_id.as_local() == Some(self.mir_def_id()) && upvar_field.is_some() =>
|
||||
if def_id.as_local() == Some(self.mir_def_id())
|
||||
&& let Some(upvar_field) = upvar_field =>
|
||||
{
|
||||
let closure_kind_ty = closure_args.as_closure().kind_ty();
|
||||
let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
|
||||
|
|
@ -495,7 +488,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let capture_description =
|
||||
format!("captured variable in an `{closure_kind}` closure");
|
||||
|
||||
let upvar = &self.upvars[upvar_field.unwrap().index()];
|
||||
let upvar = &self.upvars[upvar_field.index()];
|
||||
let upvar_hir_id = upvar.get_root_variable();
|
||||
let upvar_name = upvar.to_string(tcx);
|
||||
let upvar_span = tcx.hir_span(upvar_hir_id);
|
||||
|
|
@ -605,7 +598,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
// No binding. Nothing to suggest.
|
||||
GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
|
||||
let use_span = use_spans.var_or_use();
|
||||
let mut use_span = use_spans.var_or_use();
|
||||
let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
|
||||
let place_desc = match self.describe_place(original_path.as_ref()) {
|
||||
Some(desc) => format!("`{desc}`"),
|
||||
|
|
@ -622,6 +615,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(upvar_field) = self
|
||||
.prefixes(original_path.as_ref(), PrefixSet::All)
|
||||
.find_map(|p| self.is_upvar_field_projection(p))
|
||||
{
|
||||
// Look for the introduction of the original binding being moved.
|
||||
let upvar = &self.upvars[upvar_field.index()];
|
||||
let upvar_hir_id = upvar.get_root_variable();
|
||||
use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) {
|
||||
hir::Node::Param(param) => {
|
||||
// Instead of pointing at the path where we access the value within a
|
||||
// closure, we point at the type of the outer `fn` argument.
|
||||
param.ty_span
|
||||
}
|
||||
hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) {
|
||||
// We point at the type of the outer let-binding.
|
||||
(Some(ty), _) => ty.span,
|
||||
// We point at the initializer of the outer let-binding, but only if it
|
||||
// isn't something that spans multiple lines, like a closure, as the
|
||||
// ASCII art gets messy.
|
||||
(None, Some(init))
|
||||
if !self.infcx.tcx.sess.source_map().is_multiline(init.span) =>
|
||||
{
|
||||
init.span
|
||||
}
|
||||
_ => use_span,
|
||||
},
|
||||
_ => use_span,
|
||||
};
|
||||
}
|
||||
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
is_partial_move: false,
|
||||
ty: place_ty,
|
||||
|
|
@ -629,12 +652,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
span: use_span,
|
||||
});
|
||||
|
||||
let mut pointed_at_span = false;
|
||||
use_spans.args_subdiag(err, |args_span| {
|
||||
if args_span == span || args_span == use_span {
|
||||
pointed_at_span = true;
|
||||
}
|
||||
crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
|
||||
place: place_desc,
|
||||
place: place_desc.clone(),
|
||||
args_span,
|
||||
}
|
||||
});
|
||||
if !pointed_at_span && use_span != span {
|
||||
err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
|
||||
place: place_desc,
|
||||
args_span: span,
|
||||
});
|
||||
}
|
||||
|
||||
self.add_note_for_packed_struct_derive(err, original_path.local);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ pub(crate) fn codegen_tls_ref<'tcx>(
|
|||
pub(crate) fn eval_mir_constant<'tcx>(
|
||||
fx: &FunctionCx<'_, '_, 'tcx>,
|
||||
constant: &ConstOperand<'tcx>,
|
||||
) -> (ConstValue<'tcx>, Ty<'tcx>) {
|
||||
) -> (ConstValue, Ty<'tcx>) {
|
||||
let cv = fx.monomorphize(constant.const_);
|
||||
// This cannot fail because we checked all required_consts in advance.
|
||||
let val = cv
|
||||
|
|
@ -93,7 +93,7 @@ pub(crate) fn codegen_constant_operand<'tcx>(
|
|||
|
||||
pub(crate) fn codegen_const_value<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
const_val: ConstValue<'tcx>,
|
||||
const_val: ConstValue,
|
||||
ty: Ty<'tcx>,
|
||||
) -> CValue<'tcx> {
|
||||
let layout = fx.layout_of(ty);
|
||||
|
|
@ -210,8 +210,7 @@ pub(crate) fn codegen_const_value<'tcx>(
|
|||
.offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
|
||||
layout,
|
||||
),
|
||||
ConstValue::Slice { data, meta } => {
|
||||
let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
|
||||
ConstValue::Slice { alloc_id, meta } => {
|
||||
let ptr = pointer_for_allocation(fx, alloc_id).get_addr(fx);
|
||||
let len = fx.bcx.ins().iconst(fx.pointer_type, meta as i64);
|
||||
CValue::by_val_pair(ptr, len, layout)
|
||||
|
|
|
|||
|
|
@ -588,7 +588,7 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
&"always",
|
||||
&"--stage",
|
||||
&"0",
|
||||
&"tests/assembly/asm",
|
||||
&"tests/assembly-llvm/asm",
|
||||
&"--compiletest-rustc-args",
|
||||
&rustc_args,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -3,12 +3,4 @@ codegen_gcc_unwinding_inline_asm =
|
|||
|
||||
codegen_gcc_copy_bitcode = failed to copy bitcode to object file: {$err}
|
||||
|
||||
codegen_gcc_dynamic_linking_with_lto =
|
||||
cannot prefer dynamic linking when performing LTO
|
||||
.note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
|
||||
|
||||
codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs
|
||||
|
||||
codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
|
||||
|
||||
codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
|
||||
|
|
|
|||
|
|
@ -25,35 +25,21 @@ use std::sync::Arc;
|
|||
use gccjit::{Context, OutputKind};
|
||||
use object::read::archive::ArchiveFile;
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::symbol_export;
|
||||
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_errors::{DiagCtxtHandle, FatalError};
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
|
||||
use rustc_session::config::{CrateType, Lto};
|
||||
use rustc_session::config::Lto;
|
||||
use rustc_target::spec::RelocModel;
|
||||
use tempfile::{TempDir, tempdir};
|
||||
|
||||
use crate::back::write::save_temp_bitcode;
|
||||
use crate::errors::{DynamicLinkingWithLTO, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib};
|
||||
use crate::errors::LtoBitcodeFromRlib;
|
||||
use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level};
|
||||
|
||||
pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
|
||||
match crate_type {
|
||||
CrateType::Executable
|
||||
| CrateType::Dylib
|
||||
| CrateType::Staticlib
|
||||
| CrateType::Cdylib
|
||||
| CrateType::Sdylib => true,
|
||||
CrateType::Rlib | CrateType::ProcMacro => false,
|
||||
}
|
||||
}
|
||||
|
||||
struct LtoData {
|
||||
// TODO(antoyo): use symbols_below_threshold.
|
||||
//symbols_below_threshold: Vec<String>,
|
||||
|
|
@ -63,18 +49,9 @@ struct LtoData {
|
|||
|
||||
fn prepare_lto(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
) -> Result<LtoData, FatalError> {
|
||||
let export_threshold = match cgcx.lto {
|
||||
// We're just doing LTO for our one crate
|
||||
Lto::ThinLocal => SymbolExportLevel::Rust,
|
||||
|
||||
// We're doing LTO for the entire crate graph
|
||||
Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types),
|
||||
|
||||
Lto::No => panic!("didn't request LTO but we're doing LTO"),
|
||||
};
|
||||
|
||||
let tmp_path = match tempdir() {
|
||||
Ok(tmp_path) => tmp_path,
|
||||
Err(error) => {
|
||||
|
|
@ -83,20 +60,6 @@ fn prepare_lto(
|
|||
}
|
||||
};
|
||||
|
||||
let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
|
||||
if info.level.is_below_threshold(export_threshold) || info.used {
|
||||
Some(name.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
|
||||
let mut symbols_below_threshold = {
|
||||
let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold");
|
||||
exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::<Vec<String>>()
|
||||
};
|
||||
info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
|
||||
|
||||
// If we're performing LTO for the entire crate graph, then for each of our
|
||||
// upstream dependencies, find the corresponding rlib and load the bitcode
|
||||
// from the archive.
|
||||
|
|
@ -105,32 +68,7 @@ fn prepare_lto(
|
|||
// with either fat or thin LTO
|
||||
let mut upstream_modules = Vec::new();
|
||||
if cgcx.lto != Lto::ThinLocal {
|
||||
// Make sure we actually can run LTO
|
||||
for crate_type in cgcx.crate_types.iter() {
|
||||
if !crate_type_allows_lto(*crate_type) {
|
||||
dcx.emit_err(LtoDisallowed);
|
||||
return Err(FatalError);
|
||||
}
|
||||
if *crate_type == CrateType::Dylib && !cgcx.opts.unstable_opts.dylib_lto {
|
||||
dcx.emit_err(LtoDylib);
|
||||
return Err(FatalError);
|
||||
}
|
||||
}
|
||||
|
||||
if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
|
||||
dcx.emit_err(DynamicLinkingWithLTO);
|
||||
return Err(FatalError);
|
||||
}
|
||||
|
||||
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
|
||||
let exported_symbols =
|
||||
cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
|
||||
{
|
||||
let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold");
|
||||
symbols_below_threshold
|
||||
.extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
|
||||
}
|
||||
|
||||
for path in each_linked_rlib_for_lto {
|
||||
let archive_data = unsafe {
|
||||
Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib")
|
||||
};
|
||||
|
|
@ -174,19 +112,18 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> {
|
|||
/// for further optimization.
|
||||
pub(crate) fn run_fat(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<GccCodegenBackend>>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<ModuleCodegen<GccContext>, FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let lto_data = prepare_lto(cgcx, dcx)?;
|
||||
let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx)?;
|
||||
/*let symbols_below_threshold =
|
||||
lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();*/
|
||||
fat_lto(
|
||||
cgcx,
|
||||
dcx,
|
||||
modules,
|
||||
cached_modules,
|
||||
lto_data.upstream_modules,
|
||||
lto_data.tmp_path,
|
||||
//<o_data.symbols_below_threshold,
|
||||
|
|
@ -197,7 +134,6 @@ fn fat_lto(
|
|||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
_dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<FatLtoInput<GccCodegenBackend>>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
tmp_path: TempDir,
|
||||
//symbols_below_threshold: &[String],
|
||||
|
|
@ -211,21 +147,12 @@ fn fat_lto(
|
|||
// modules that are serialized in-memory.
|
||||
// * `in_memory` contains modules which are already parsed and in-memory,
|
||||
// such as from multi-CGU builds.
|
||||
//
|
||||
// All of `cached_modules` (cached from previous incremental builds) can
|
||||
// immediately go onto the `serialized_modules` modules list and then we can
|
||||
// split the `modules` array into these two lists.
|
||||
let mut in_memory = Vec::new();
|
||||
serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| {
|
||||
info!("pushing cached module {:?}", wp.cgu_name);
|
||||
(buffer, CString::new(wp.cgu_name).unwrap())
|
||||
}));
|
||||
for module in modules {
|
||||
match module {
|
||||
FatLtoInput::InMemory(m) => in_memory.push(m),
|
||||
FatLtoInput::Serialized { name, buffer } => {
|
||||
info!("pushing serialized module {:?}", name);
|
||||
let buffer = SerializedModule::Local(buffer);
|
||||
serialized_modules.push((buffer, CString::new(name).unwrap()));
|
||||
}
|
||||
}
|
||||
|
|
@ -356,12 +283,13 @@ impl ModuleBufferMethods for ModuleBuffer {
|
|||
/// can simply be copied over from the incr. comp. cache.
|
||||
pub(crate) fn run_thin(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<(String, ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let lto_data = prepare_lto(cgcx, dcx)?;
|
||||
let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx)?;
|
||||
if cgcx.opts.cg.linker_plugin_lto.enabled() {
|
||||
unreachable!(
|
||||
"We should never reach this case if the LTO step \
|
||||
|
|
|
|||
|
|
@ -14,19 +14,6 @@ pub(crate) struct CopyBitcode {
|
|||
pub err: std::io::Error,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_dynamic_linking_with_lto)]
|
||||
#[note]
|
||||
pub(crate) struct DynamicLinkingWithLTO;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_lto_disallowed)]
|
||||
pub(crate) struct LtoDisallowed;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_lto_dylib)]
|
||||
pub(crate) struct LtoDylib;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_gcc_lto_bitcode_from_rlib)]
|
||||
pub(crate) struct LtoBitcodeFromRlib {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ mod type_of;
|
|||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(not(feature = "master"))]
|
||||
use std::sync::atomic::AtomicBool;
|
||||
#[cfg(not(feature = "master"))]
|
||||
|
|
@ -358,23 +359,28 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
// FIXME(bjorn3): Limit LTO exports to these symbols
|
||||
_exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
if !diff_fncs.is_empty() {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
back::lto::run_fat(cgcx, modules, cached_modules)
|
||||
back::lto::run_fat(cgcx, each_linked_rlib_for_lto, modules)
|
||||
}
|
||||
|
||||
fn run_thin_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
// FIXME(bjorn3): Limit LTO exports to these symbols
|
||||
_exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<(String, Self::ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> {
|
||||
back::lto::run_thin(cgcx, modules, cached_modules)
|
||||
back::lto::run_thin(cgcx, each_linked_rlib_for_lto, modules, cached_modules)
|
||||
}
|
||||
|
||||
fn print_pass_timings(&self) {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@ codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z au
|
|||
|
||||
codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err}
|
||||
|
||||
codegen_llvm_dynamic_linking_with_lto =
|
||||
cannot prefer dynamic linking when performing LTO
|
||||
.note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
|
||||
|
||||
|
||||
codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
|
||||
|
||||
|
|
@ -18,12 +14,6 @@ codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$na
|
|||
|
||||
codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$llvm_err})
|
||||
|
||||
codegen_llvm_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs
|
||||
|
||||
codegen_llvm_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
|
||||
|
||||
codegen_llvm_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto`
|
||||
|
||||
codegen_llvm_mismatch_data_layout =
|
||||
data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,33 +1,28 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::Arc;
|
||||
use std::{io, iter, slice};
|
||||
|
||||
use object::read::archive::ArchiveFile;
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::symbol_export;
|
||||
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_errors::{DiagCtxtHandle, FatalError};
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
|
||||
use rustc_session::config::{self, CrateType, Lto};
|
||||
use rustc_session::config::{self, Lto};
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::back::write::{
|
||||
self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode,
|
||||
};
|
||||
use crate::errors::{
|
||||
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro,
|
||||
};
|
||||
use crate::errors::{LlvmError, LtoBitcodeFromRlib};
|
||||
use crate::llvm::AttributePlace::Function;
|
||||
use crate::llvm::{self, build_string};
|
||||
use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes};
|
||||
|
|
@ -36,45 +31,21 @@ use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes};
|
|||
/// session to determine which CGUs we can reuse.
|
||||
const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin";
|
||||
|
||||
fn crate_type_allows_lto(crate_type: CrateType) -> bool {
|
||||
match crate_type {
|
||||
CrateType::Executable
|
||||
| CrateType::Dylib
|
||||
| CrateType::Staticlib
|
||||
| CrateType::Cdylib
|
||||
| CrateType::ProcMacro
|
||||
| CrateType::Sdylib => true,
|
||||
CrateType::Rlib => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_lto(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
) -> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError> {
|
||||
let export_threshold = match cgcx.lto {
|
||||
// We're just doing LTO for our one crate
|
||||
Lto::ThinLocal => SymbolExportLevel::Rust,
|
||||
let mut symbols_below_threshold = exported_symbols_for_lto
|
||||
.iter()
|
||||
.map(|symbol| CString::new(symbol.to_owned()).unwrap())
|
||||
.collect::<Vec<CString>>();
|
||||
|
||||
// We're doing LTO for the entire crate graph
|
||||
Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types),
|
||||
|
||||
Lto::No => panic!("didn't request LTO but we're doing LTO"),
|
||||
};
|
||||
|
||||
let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
|
||||
if info.level.is_below_threshold(export_threshold) || info.used {
|
||||
Some(CString::new(name.as_str()).unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
|
||||
let mut symbols_below_threshold = {
|
||||
let _timer = cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold");
|
||||
exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::<Vec<CString>>()
|
||||
};
|
||||
info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
|
||||
// __llvm_profile_counter_bias is pulled in at link time by an undefined reference to
|
||||
// __llvm_profile_runtime, therefore we won't know until link time if this symbol
|
||||
// should have default visibility.
|
||||
symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned());
|
||||
|
||||
// If we're performing LTO for the entire crate graph, then for each of our
|
||||
// upstream dependencies, find the corresponding rlib and load the bitcode
|
||||
|
|
@ -84,37 +55,7 @@ fn prepare_lto(
|
|||
// with either fat or thin LTO
|
||||
let mut upstream_modules = Vec::new();
|
||||
if cgcx.lto != Lto::ThinLocal {
|
||||
// Make sure we actually can run LTO
|
||||
for crate_type in cgcx.crate_types.iter() {
|
||||
if !crate_type_allows_lto(*crate_type) {
|
||||
dcx.emit_err(LtoDisallowed);
|
||||
return Err(FatalError);
|
||||
} else if *crate_type == CrateType::Dylib {
|
||||
if !cgcx.opts.unstable_opts.dylib_lto {
|
||||
dcx.emit_err(LtoDylib);
|
||||
return Err(FatalError);
|
||||
}
|
||||
} else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto {
|
||||
dcx.emit_err(LtoProcMacro);
|
||||
return Err(FatalError);
|
||||
}
|
||||
}
|
||||
|
||||
if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
|
||||
dcx.emit_err(DynamicLinkingWithLTO);
|
||||
return Err(FatalError);
|
||||
}
|
||||
|
||||
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
|
||||
let exported_symbols =
|
||||
cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
|
||||
{
|
||||
let _timer =
|
||||
cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold");
|
||||
symbols_below_threshold
|
||||
.extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
|
||||
}
|
||||
|
||||
for path in each_linked_rlib_for_lto {
|
||||
let archive_data = unsafe {
|
||||
Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib"))
|
||||
.expect("couldn't map rlib")
|
||||
|
|
@ -147,10 +88,6 @@ fn prepare_lto(
|
|||
}
|
||||
}
|
||||
|
||||
// __llvm_profile_counter_bias is pulled in at link time by an undefined reference to
|
||||
// __llvm_profile_runtime, therefore we won't know until link time if this symbol
|
||||
// should have default visibility.
|
||||
symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned());
|
||||
Ok((symbols_below_threshold, upstream_modules))
|
||||
}
|
||||
|
||||
|
|
@ -199,15 +136,17 @@ fn get_bitcode_slice_from_object_data<'a>(
|
|||
/// for further optimization.
|
||||
pub(crate) fn run_fat(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
|
||||
let (symbols_below_threshold, upstream_modules) =
|
||||
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?;
|
||||
let symbols_below_threshold =
|
||||
symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
|
||||
fat_lto(cgcx, dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold)
|
||||
fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold)
|
||||
}
|
||||
|
||||
/// Performs thin LTO by performing necessary global analysis and returning two
|
||||
|
|
@ -215,12 +154,15 @@ pub(crate) fn run_fat(
|
|||
/// can simply be copied over from the incr. comp. cache.
|
||||
pub(crate) fn run_thin(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<(String, ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
|
||||
let (symbols_below_threshold, upstream_modules) =
|
||||
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?;
|
||||
let symbols_below_threshold =
|
||||
symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
|
||||
if cgcx.opts.cg.linker_plugin_lto.enabled() {
|
||||
|
|
@ -245,7 +187,6 @@ fn fat_lto(
|
|||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
|
||||
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
||||
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
||||
symbols_below_threshold: &[*const libc::c_char],
|
||||
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
|
||||
|
|
@ -258,21 +199,12 @@ fn fat_lto(
|
|||
// modules that are serialized in-memory.
|
||||
// * `in_memory` contains modules which are already parsed and in-memory,
|
||||
// such as from multi-CGU builds.
|
||||
//
|
||||
// All of `cached_modules` (cached from previous incremental builds) can
|
||||
// immediately go onto the `serialized_modules` modules list and then we can
|
||||
// split the `modules` array into these two lists.
|
||||
let mut in_memory = Vec::new();
|
||||
serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| {
|
||||
info!("pushing cached module {:?}", wp.cgu_name);
|
||||
(buffer, CString::new(wp.cgu_name).unwrap())
|
||||
}));
|
||||
for module in modules {
|
||||
match module {
|
||||
FatLtoInput::InMemory(m) => in_memory.push(m),
|
||||
FatLtoInput::Serialized { name, buffer } => {
|
||||
info!("pushing serialized module {:?}", name);
|
||||
let buffer = SerializedModule::Local(buffer);
|
||||
serialized_modules.push((buffer, CString::new(name).unwrap()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,10 @@ impl Coords {
|
|||
/// or other expansions), and if it does happen then skipping a span or function is
|
||||
/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid.
|
||||
pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span) -> Option<Coords> {
|
||||
let span = ensure_non_empty_span(source_map, span)?;
|
||||
if span.is_empty() {
|
||||
debug_assert!(false, "can't make coords from empty span: {span:?}");
|
||||
return None;
|
||||
}
|
||||
|
||||
let lo = span.lo();
|
||||
let hi = span.hi();
|
||||
|
|
@ -70,29 +73,6 @@ pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span)
|
|||
})
|
||||
}
|
||||
|
||||
fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
|
||||
if !span.is_empty() {
|
||||
return Some(span);
|
||||
}
|
||||
|
||||
// The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
|
||||
source_map
|
||||
.span_to_source(span, |src, start, end| try {
|
||||
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
|
||||
// but in this case we have specifically checked that the character
|
||||
// we're skipping over is one of two specific ASCII characters, so
|
||||
// adjusting by exactly 1 byte is correct.
|
||||
if src.as_bytes().get(end).copied() == Some(b'{') {
|
||||
Some(span.with_hi(span.hi() + BytePos(1)))
|
||||
} else if start > 0 && src.as_bytes()[start - 1] == b'}' {
|
||||
Some(span.with_lo(span.lo() - BytePos(1)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok()?
|
||||
}
|
||||
|
||||
/// If `llvm-cov` sees a source region that is improperly ordered (end < start),
|
||||
/// it will immediately exit with a fatal error. To prevent that from happening,
|
||||
/// discard regions that are improperly ordered, or might be interpreted in a
|
||||
|
|
|
|||
|
|
@ -20,11 +20,6 @@ pub(crate) struct SymbolAlreadyDefined<'a> {
|
|||
#[diag(codegen_llvm_sanitizer_memtag_requires_mte)]
|
||||
pub(crate) struct SanitizerMemtagRequiresMte;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_dynamic_linking_with_lto)]
|
||||
#[note]
|
||||
pub(crate) struct DynamicLinkingWithLTO;
|
||||
|
||||
pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>);
|
||||
|
||||
impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
|
||||
|
|
@ -41,18 +36,6 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
|
|||
#[diag(codegen_llvm_autodiff_without_enable)]
|
||||
pub(crate) struct AutoDiffWithoutEnable;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_disallowed)]
|
||||
pub(crate) struct LtoDisallowed;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_dylib)]
|
||||
pub(crate) struct LtoDylib;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_proc_macro)]
|
||||
pub(crate) struct LtoProcMacro;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_bitcode_from_rlib)]
|
||||
pub(crate) struct LtoBitcodeFromRlib {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
use std::any::Any;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use back::owned_target_machine::OwnedTargetMachine;
|
||||
use back::write::{create_informational_target_machine, create_target_machine};
|
||||
|
|
@ -176,11 +177,13 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
}
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
let mut module = back::lto::run_fat(cgcx, modules, cached_modules)?;
|
||||
let mut module =
|
||||
back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules)?;
|
||||
|
||||
if !diff_fncs.is_empty() {
|
||||
builder::autodiff::differentiate(&module, cgcx, diff_fncs)?;
|
||||
|
|
@ -194,10 +197,18 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
}
|
||||
fn run_thin_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<(String, Self::ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> {
|
||||
back::lto::run_thin(cgcx, modules, cached_modules)
|
||||
back::lto::run_thin(
|
||||
cgcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
modules,
|
||||
cached_modules,
|
||||
)
|
||||
}
|
||||
fn optimize(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ codegen_ssa_dlltool_fail_import_library =
|
|||
{$stdout}
|
||||
{$stderr}
|
||||
|
||||
codegen_ssa_dynamic_linking_with_lto =
|
||||
cannot prefer dynamic linking when performing LTO
|
||||
.note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
|
||||
|
||||
codegen_ssa_error_calling_dlltool =
|
||||
Error calling dlltool '{$dlltool_path}': {$error}
|
||||
|
||||
|
|
@ -191,6 +195,12 @@ codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for
|
|||
|
||||
codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status}
|
||||
|
||||
codegen_ssa_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs
|
||||
|
||||
codegen_ssa_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
|
||||
|
||||
codegen_ssa_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto`
|
||||
|
||||
codegen_ssa_malformed_cgu_name =
|
||||
found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case).
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use rustc_abi::Endian;
|
||||
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_data_structures::stable_hasher::StableHasher;
|
||||
use rustc_hashes::Hash128;
|
||||
use rustc_session::Session;
|
||||
|
|
@ -214,7 +214,7 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
|
|||
/// It exports all the provided symbols, but is otherwise empty.
|
||||
fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> {
|
||||
use object::write::elf as write;
|
||||
use object::{Architecture, elf};
|
||||
use object::{AddressSize, Architecture, elf};
|
||||
|
||||
let mut stub_buf = Vec::new();
|
||||
|
||||
|
|
@ -226,47 +226,6 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
|||
// It is important that the order of reservation matches the order of writing.
|
||||
// The object crate contains many debug asserts that fire if you get this wrong.
|
||||
|
||||
let endianness = match sess.target.options.endian {
|
||||
Endian::Little => object::Endianness::Little,
|
||||
Endian::Big => object::Endianness::Big,
|
||||
};
|
||||
let mut stub = write::Writer::new(endianness, true, &mut stub_buf);
|
||||
|
||||
// These initial reservations don't reserve any bytes in the binary yet,
|
||||
// they just allocate in the internal data structures.
|
||||
|
||||
// First, we crate the dynamic symbol table. It starts with a null symbol
|
||||
// and then all the symbols and their dynamic strings.
|
||||
stub.reserve_null_dynamic_symbol_index();
|
||||
|
||||
let dynstrs = symbols
|
||||
.iter()
|
||||
.map(|sym| {
|
||||
stub.reserve_dynamic_symbol_index();
|
||||
(sym, stub.add_dynamic_string(sym.name.as_str().as_bytes()))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let soname = stub.add_dynamic_string(soname.as_bytes());
|
||||
|
||||
// Reserve the sections.
|
||||
// We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to.
|
||||
stub.reserve_shstrtab_section_index();
|
||||
let text_section_name = stub.add_section_name(".text".as_bytes());
|
||||
let text_section = stub.reserve_section_index();
|
||||
stub.reserve_dynstr_section_index();
|
||||
stub.reserve_dynsym_section_index();
|
||||
stub.reserve_dynamic_section_index();
|
||||
|
||||
// These reservations now determine the actual layout order of the object file.
|
||||
stub.reserve_file_header();
|
||||
stub.reserve_shstrtab();
|
||||
stub.reserve_section_headers();
|
||||
stub.reserve_dynstr();
|
||||
stub.reserve_dynsym();
|
||||
stub.reserve_dynamic(2); // DT_SONAME, DT_NULL
|
||||
|
||||
// First write the ELF header with the arch information.
|
||||
let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
|
||||
else {
|
||||
sess.dcx().fatal(format!(
|
||||
|
|
@ -274,6 +233,87 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
|||
sess.target.arch
|
||||
));
|
||||
};
|
||||
|
||||
let endianness = match sess.target.options.endian {
|
||||
Endian::Little => object::Endianness::Little,
|
||||
Endian::Big => object::Endianness::Big,
|
||||
};
|
||||
|
||||
let is_64 = match arch.address_size() {
|
||||
Some(AddressSize::U8 | AddressSize::U16 | AddressSize::U32) => false,
|
||||
Some(AddressSize::U64) => true,
|
||||
_ => sess.dcx().fatal(format!(
|
||||
"raw-dylib is not supported for the architecture `{}`",
|
||||
sess.target.arch
|
||||
)),
|
||||
};
|
||||
|
||||
let mut stub = write::Writer::new(endianness, is_64, &mut stub_buf);
|
||||
|
||||
let mut vers = Vec::new();
|
||||
let mut vers_map = FxHashMap::default();
|
||||
let mut syms = Vec::new();
|
||||
|
||||
for symbol in symbols {
|
||||
let symbol_name = symbol.name.as_str();
|
||||
if let Some((name, version_name)) = symbol_name.split_once('@') {
|
||||
assert!(!version_name.contains('@'));
|
||||
let dynstr = stub.add_dynamic_string(name.as_bytes());
|
||||
let ver = if let Some(&ver_id) = vers_map.get(version_name) {
|
||||
ver_id
|
||||
} else {
|
||||
let id = vers.len();
|
||||
vers_map.insert(version_name, id);
|
||||
let dynstr = stub.add_dynamic_string(version_name.as_bytes());
|
||||
vers.push((version_name, dynstr));
|
||||
id
|
||||
};
|
||||
syms.push((name, dynstr, Some(ver)));
|
||||
} else {
|
||||
let dynstr = stub.add_dynamic_string(symbol_name.as_bytes());
|
||||
syms.push((symbol_name, dynstr, None));
|
||||
}
|
||||
}
|
||||
|
||||
let soname = stub.add_dynamic_string(soname.as_bytes());
|
||||
|
||||
// These initial reservations don't reserve any bytes in the binary yet,
|
||||
// they just allocate in the internal data structures.
|
||||
|
||||
// First, we create the dynamic symbol table. It starts with a null symbol
|
||||
// and then all the symbols and their dynamic strings.
|
||||
stub.reserve_null_dynamic_symbol_index();
|
||||
|
||||
for _ in syms.iter() {
|
||||
stub.reserve_dynamic_symbol_index();
|
||||
}
|
||||
|
||||
// Reserve the sections.
|
||||
// We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to.
|
||||
stub.reserve_shstrtab_section_index();
|
||||
let text_section_name = stub.add_section_name(".text".as_bytes());
|
||||
let text_section = stub.reserve_section_index();
|
||||
stub.reserve_dynsym_section_index();
|
||||
stub.reserve_dynstr_section_index();
|
||||
if !vers.is_empty() {
|
||||
stub.reserve_gnu_versym_section_index();
|
||||
stub.reserve_gnu_verdef_section_index();
|
||||
}
|
||||
stub.reserve_dynamic_section_index();
|
||||
|
||||
// These reservations now determine the actual layout order of the object file.
|
||||
stub.reserve_file_header();
|
||||
stub.reserve_shstrtab();
|
||||
stub.reserve_section_headers();
|
||||
stub.reserve_dynsym();
|
||||
stub.reserve_dynstr();
|
||||
if !vers.is_empty() {
|
||||
stub.reserve_gnu_versym();
|
||||
stub.reserve_gnu_verdef(1 + vers.len(), 1 + vers.len());
|
||||
}
|
||||
stub.reserve_dynamic(2); // DT_SONAME, DT_NULL
|
||||
|
||||
// First write the ELF header with the arch information.
|
||||
let e_machine = match (arch, sub_arch) {
|
||||
(Architecture::Aarch64, None) => elf::EM_AARCH64,
|
||||
(Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
|
||||
|
|
@ -342,18 +382,19 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
|||
sh_addralign: 1,
|
||||
sh_entsize: 0,
|
||||
});
|
||||
stub.write_dynstr_section_header(0);
|
||||
stub.write_dynsym_section_header(0, 1);
|
||||
stub.write_dynstr_section_header(0);
|
||||
if !vers.is_empty() {
|
||||
stub.write_gnu_versym_section_header(0);
|
||||
stub.write_gnu_verdef_section_header(0);
|
||||
}
|
||||
stub.write_dynamic_section_header(0);
|
||||
|
||||
// .dynstr
|
||||
stub.write_dynstr();
|
||||
|
||||
// .dynsym
|
||||
stub.write_null_dynamic_symbol();
|
||||
for (_, name) in dynstrs {
|
||||
for (_name, dynstr, _ver) in syms.iter().copied() {
|
||||
stub.write_dynamic_symbol(&write::Sym {
|
||||
name: Some(name),
|
||||
name: Some(dynstr),
|
||||
st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
|
||||
st_other: elf::STV_DEFAULT,
|
||||
section: Some(text_section),
|
||||
|
|
@ -363,10 +404,47 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
|||
});
|
||||
}
|
||||
|
||||
// .dynstr
|
||||
stub.write_dynstr();
|
||||
|
||||
// ld.bfd is unhappy if these sections exist without any symbols, so we only generate them when necessary.
|
||||
if !vers.is_empty() {
|
||||
// .gnu_version
|
||||
stub.write_null_gnu_versym();
|
||||
for (_name, _dynstr, ver) in syms.iter().copied() {
|
||||
stub.write_gnu_versym(if let Some(ver) = ver {
|
||||
assert!((2 + ver as u16) < elf::VERSYM_HIDDEN);
|
||||
elf::VERSYM_HIDDEN | (2 + ver as u16)
|
||||
} else {
|
||||
1
|
||||
});
|
||||
}
|
||||
|
||||
// .gnu_version_d
|
||||
stub.write_align_gnu_verdef();
|
||||
stub.write_gnu_verdef(&write::Verdef {
|
||||
version: elf::VER_DEF_CURRENT,
|
||||
flags: elf::VER_FLG_BASE,
|
||||
index: 1,
|
||||
aux_count: 1,
|
||||
name: soname,
|
||||
});
|
||||
for (ver, (_name, dynstr)) in vers.into_iter().enumerate() {
|
||||
stub.write_gnu_verdef(&write::Verdef {
|
||||
version: elf::VER_DEF_CURRENT,
|
||||
flags: 0,
|
||||
index: 2 + ver as u16,
|
||||
aux_count: 1,
|
||||
name: dynstr,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// .dynamic
|
||||
// the DT_SONAME will be used by the linker to populate DT_NEEDED
|
||||
// which the loader uses to find the library.
|
||||
// DT_NULL terminates the .dynamic table.
|
||||
stub.write_align_dynamic();
|
||||
stub.write_dynamic_string(elf::DT_SONAME, soname);
|
||||
stub.write_dynamic(elf::DT_NULL, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,15 @@ use std::ffi::CString;
|
|||
use std::sync::Arc;
|
||||
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::{CrateType, Lto};
|
||||
use tracing::info;
|
||||
|
||||
use crate::back::symbol_export::{self, symbol_name_for_instance_in_crate};
|
||||
use crate::back::write::CodegenContext;
|
||||
use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro};
|
||||
use crate::traits::*;
|
||||
|
||||
pub struct ThinModule<B: WriteBackendMethods> {
|
||||
|
|
@ -52,3 +60,86 @@ impl<M: ModuleBufferMethods> SerializedModule<M> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn crate_type_allows_lto(crate_type: CrateType) -> bool {
|
||||
match crate_type {
|
||||
CrateType::Executable
|
||||
| CrateType::Dylib
|
||||
| CrateType::Staticlib
|
||||
| CrateType::Cdylib
|
||||
| CrateType::ProcMacro
|
||||
| CrateType::Sdylib => true,
|
||||
CrateType::Rlib => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn exported_symbols_for_lto(
|
||||
tcx: TyCtxt<'_>,
|
||||
each_linked_rlib_for_lto: &[CrateNum],
|
||||
) -> Vec<String> {
|
||||
let export_threshold = match tcx.sess.lto() {
|
||||
// We're just doing LTO for our one crate
|
||||
Lto::ThinLocal => SymbolExportLevel::Rust,
|
||||
|
||||
// We're doing LTO for the entire crate graph
|
||||
Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&tcx.crate_types()),
|
||||
|
||||
Lto::No => return vec![],
|
||||
};
|
||||
|
||||
let copy_symbols = |cnum| {
|
||||
tcx.exported_non_generic_symbols(cnum)
|
||||
.iter()
|
||||
.chain(tcx.exported_generic_symbols(cnum))
|
||||
.filter_map(|&(s, info): &(ExportedSymbol<'_>, SymbolExportInfo)| {
|
||||
if info.level.is_below_threshold(export_threshold) || info.used {
|
||||
Some(symbol_name_for_instance_in_crate(tcx, s, cnum))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let mut symbols_below_threshold = {
|
||||
let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold");
|
||||
copy_symbols(LOCAL_CRATE)
|
||||
};
|
||||
info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
|
||||
|
||||
// If we're performing LTO for the entire crate graph, then for each of our
|
||||
// upstream dependencies, include their exported symbols.
|
||||
if tcx.sess.lto() != Lto::ThinLocal {
|
||||
for &cnum in each_linked_rlib_for_lto {
|
||||
let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold");
|
||||
symbols_below_threshold.extend(copy_symbols(cnum));
|
||||
}
|
||||
}
|
||||
|
||||
symbols_below_threshold
|
||||
}
|
||||
|
||||
pub(super) fn check_lto_allowed<B: WriteBackendMethods>(cgcx: &CodegenContext<B>) {
|
||||
if cgcx.lto == Lto::ThinLocal {
|
||||
// Crate local LTO is always allowed
|
||||
return;
|
||||
}
|
||||
|
||||
let dcx = cgcx.create_dcx();
|
||||
|
||||
// Make sure we actually can run LTO
|
||||
for crate_type in cgcx.crate_types.iter() {
|
||||
if !crate_type_allows_lto(*crate_type) {
|
||||
dcx.handle().emit_fatal(LtoDisallowed);
|
||||
} else if *crate_type == CrateType::Dylib {
|
||||
if !cgcx.opts.unstable_opts.dylib_lto {
|
||||
dcx.handle().emit_fatal(LtoDylib);
|
||||
}
|
||||
} else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto {
|
||||
dcx.handle().emit_fatal(LtoProcMacro);
|
||||
}
|
||||
}
|
||||
|
||||
if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
|
||||
dcx.handle().emit_fatal(DynamicLinkingWithLTO);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use std::{fs, io, mem, str, thread};
|
|||
use rustc_abi::Size;
|
||||
use rustc_ast::attr;
|
||||
use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::jobserver::{self, Acquired};
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
|
||||
|
|
@ -20,14 +20,12 @@ use rustc_errors::{
|
|||
Suggestions,
|
||||
};
|
||||
use rustc_fs_util::link_or_copy;
|
||||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use rustc_incremental::{
|
||||
copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
|
||||
};
|
||||
use rustc_metadata::fs::copy_to_stdout;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportInfo;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{
|
||||
|
|
@ -40,7 +38,7 @@ use tracing::debug;
|
|||
|
||||
use super::link::{self, ensure_removed};
|
||||
use super::lto::{self, SerializedModule};
|
||||
use super::symbol_export::symbol_name_for_instance_in_crate;
|
||||
use crate::back::lto::check_lto_allowed;
|
||||
use crate::errors::{AutodiffWithoutLto, ErrorCreatingRemarkDir};
|
||||
use crate::traits::*;
|
||||
use crate::{
|
||||
|
|
@ -332,8 +330,6 @@ pub type TargetMachineFactoryFn<B> = Arc<
|
|||
+ Sync,
|
||||
>;
|
||||
|
||||
type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo)>>>;
|
||||
|
||||
/// Additional resources used by optimize_and_codegen (not module specific)
|
||||
#[derive(Clone)]
|
||||
pub struct CodegenContext<B: WriteBackendMethods> {
|
||||
|
|
@ -343,10 +339,8 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
|||
pub save_temps: bool,
|
||||
pub fewer_names: bool,
|
||||
pub time_trace: bool,
|
||||
pub exported_symbols: Option<Arc<ExportedSymbols>>,
|
||||
pub opts: Arc<config::Options>,
|
||||
pub crate_types: Vec<CrateType>,
|
||||
pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>,
|
||||
pub output_filenames: Arc<OutputFilenames>,
|
||||
pub invocation_temp: Option<String>,
|
||||
pub regular_module_config: Arc<ModuleConfig>,
|
||||
|
|
@ -401,13 +395,21 @@ impl<B: WriteBackendMethods> CodegenContext<B> {
|
|||
|
||||
fn generate_thin_lto_work<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
needs_thin_lto: Vec<(String, B::ThinBuffer)>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Vec<(WorkItem<B>, u64)> {
|
||||
let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work");
|
||||
|
||||
let (lto_modules, copy_jobs) =
|
||||
B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise());
|
||||
let (lto_modules, copy_jobs) = B::run_thin_lto(
|
||||
cgcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
needs_thin_lto,
|
||||
import_only_modules,
|
||||
)
|
||||
.unwrap_or_else(|e| e.raise());
|
||||
lto_modules
|
||||
.into_iter()
|
||||
.map(|module| {
|
||||
|
|
@ -723,6 +725,8 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> {
|
|||
CopyPostLtoArtifacts(CachedModuleCodegen),
|
||||
/// Performs fat LTO on the given module.
|
||||
FatLto {
|
||||
exported_symbols_for_lto: Arc<Vec<String>>,
|
||||
each_linked_rlib_for_lto: Vec<PathBuf>,
|
||||
needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
autodiff: Vec<AutoDiffItem>,
|
||||
|
|
@ -810,7 +814,7 @@ pub(crate) enum WorkItemResult<B: WriteBackendMethods> {
|
|||
}
|
||||
|
||||
pub enum FatLtoInput<B: WriteBackendMethods> {
|
||||
Serialized { name: String, buffer: B::ModuleBuffer },
|
||||
Serialized { name: String, buffer: SerializedModule<B::ModuleBuffer> },
|
||||
InMemory(ModuleCodegen<B::Module>),
|
||||
}
|
||||
|
||||
|
|
@ -899,7 +903,10 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
|||
fs::write(&path, buffer.data()).unwrap_or_else(|e| {
|
||||
panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e);
|
||||
});
|
||||
Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { name, buffer }))
|
||||
Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized {
|
||||
name,
|
||||
buffer: SerializedModule::Local(buffer),
|
||||
}))
|
||||
}
|
||||
None => Ok(WorkItemResult::NeedsFatLto(FatLtoInput::InMemory(module))),
|
||||
},
|
||||
|
|
@ -992,12 +999,24 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
|||
|
||||
fn execute_fat_lto_work_item<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
mut needs_fat_lto: Vec<FatLtoInput<B>>,
|
||||
import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>,
|
||||
autodiff: Vec<AutoDiffItem>,
|
||||
module_config: &ModuleConfig,
|
||||
) -> Result<WorkItemResult<B>, FatalError> {
|
||||
let module = B::run_and_optimize_fat_lto(cgcx, needs_fat_lto, import_only_modules, autodiff)?;
|
||||
for (module, wp) in import_only_modules {
|
||||
needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module })
|
||||
}
|
||||
|
||||
let module = B::run_and_optimize_fat_lto(
|
||||
cgcx,
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
needs_fat_lto,
|
||||
autodiff,
|
||||
)?;
|
||||
let module = B::codegen(cgcx, module, module_config)?;
|
||||
Ok(WorkItemResult::Finished(module))
|
||||
}
|
||||
|
|
@ -1032,7 +1051,7 @@ pub(crate) enum Message<B: WriteBackendMethods> {
|
|||
|
||||
/// The backend has finished processing a work item for a codegen unit.
|
||||
/// Sent from a backend worker thread.
|
||||
WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>>, worker_id: usize },
|
||||
WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>> },
|
||||
|
||||
/// The frontend has finished generating something (backend IR or a
|
||||
/// post-LTO artifact) for a codegen unit, and it should be passed to the
|
||||
|
|
@ -1113,42 +1132,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
let autodiff_items = autodiff_items.to_vec();
|
||||
|
||||
let mut each_linked_rlib_for_lto = Vec::new();
|
||||
let mut each_linked_rlib_file_for_lto = Vec::new();
|
||||
drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| {
|
||||
if link::ignored_for_lto(sess, crate_info, cnum) {
|
||||
return;
|
||||
}
|
||||
each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
|
||||
each_linked_rlib_for_lto.push(cnum);
|
||||
each_linked_rlib_file_for_lto.push(path.to_path_buf());
|
||||
}));
|
||||
|
||||
// Compute the set of symbols we need to retain when doing LTO (if we need to)
|
||||
let exported_symbols = {
|
||||
let mut exported_symbols = FxHashMap::default();
|
||||
|
||||
let copy_symbols = |cnum| {
|
||||
let symbols = tcx
|
||||
.exported_non_generic_symbols(cnum)
|
||||
.iter()
|
||||
.chain(tcx.exported_generic_symbols(cnum))
|
||||
.map(|&(s, lvl)| (symbol_name_for_instance_in_crate(tcx, s, cnum), lvl))
|
||||
.collect();
|
||||
Arc::new(symbols)
|
||||
};
|
||||
|
||||
match sess.lto() {
|
||||
Lto::No => None,
|
||||
Lto::ThinLocal => {
|
||||
exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE));
|
||||
Some(Arc::new(exported_symbols))
|
||||
}
|
||||
Lto::Fat | Lto::Thin => {
|
||||
exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE));
|
||||
for &(cnum, ref _path) in &each_linked_rlib_for_lto {
|
||||
exported_symbols.insert(cnum, copy_symbols(cnum));
|
||||
}
|
||||
Some(Arc::new(exported_symbols))
|
||||
}
|
||||
}
|
||||
};
|
||||
let exported_symbols_for_lto =
|
||||
Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto));
|
||||
|
||||
// First up, convert our jobserver into a helper thread so we can use normal
|
||||
// mpsc channels to manage our messages and such.
|
||||
|
|
@ -1183,14 +1178,12 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
|
||||
let cgcx = CodegenContext::<B> {
|
||||
crate_types: tcx.crate_types().to_vec(),
|
||||
each_linked_rlib_for_lto,
|
||||
lto: sess.lto(),
|
||||
fewer_names: sess.fewer_names(),
|
||||
save_temps: sess.opts.cg.save_temps,
|
||||
time_trace: sess.opts.unstable_opts.llvm_time_trace,
|
||||
opts: Arc::new(sess.opts.clone()),
|
||||
prof: sess.prof.clone(),
|
||||
exported_symbols,
|
||||
remark: sess.opts.cg.remark.clone(),
|
||||
remark_dir,
|
||||
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
|
||||
|
|
@ -1350,18 +1343,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
// necessary. There's already optimizations in place to avoid sending work
|
||||
// back to the coordinator if LTO isn't requested.
|
||||
return B::spawn_named_thread(cgcx.time_trace, "coordinator".to_string(), move || {
|
||||
let mut worker_id_counter = 0;
|
||||
let mut free_worker_ids = Vec::new();
|
||||
let mut get_worker_id = |free_worker_ids: &mut Vec<usize>| {
|
||||
if let Some(id) = free_worker_ids.pop() {
|
||||
id
|
||||
} else {
|
||||
let id = worker_id_counter;
|
||||
worker_id_counter += 1;
|
||||
id
|
||||
}
|
||||
};
|
||||
|
||||
// This is where we collect codegen units that have gone all the way
|
||||
// through codegen and LLVM.
|
||||
let mut compiled_modules = vec![];
|
||||
|
|
@ -1442,12 +1423,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
let (item, _) =
|
||||
work_items.pop().expect("queue empty - queue_full_enough() broken?");
|
||||
main_thread_state = MainThreadState::Lending;
|
||||
spawn_work(
|
||||
&cgcx,
|
||||
&mut llvm_start_time,
|
||||
get_worker_id(&mut free_worker_ids),
|
||||
item,
|
||||
);
|
||||
spawn_work(&cgcx, &mut llvm_start_time, item);
|
||||
}
|
||||
}
|
||||
} else if codegen_state == Completed {
|
||||
|
|
@ -1474,12 +1450,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
let needs_fat_lto = mem::take(&mut needs_fat_lto);
|
||||
let needs_thin_lto = mem::take(&mut needs_thin_lto);
|
||||
let import_only_modules = mem::take(&mut lto_import_only_modules);
|
||||
let each_linked_rlib_file_for_lto =
|
||||
mem::take(&mut each_linked_rlib_file_for_lto);
|
||||
|
||||
check_lto_allowed(&cgcx);
|
||||
|
||||
if !needs_fat_lto.is_empty() {
|
||||
assert!(needs_thin_lto.is_empty());
|
||||
|
||||
work_items.push((
|
||||
WorkItem::FatLto {
|
||||
exported_symbols_for_lto: Arc::clone(&exported_symbols_for_lto),
|
||||
each_linked_rlib_for_lto: each_linked_rlib_file_for_lto,
|
||||
needs_fat_lto,
|
||||
import_only_modules,
|
||||
autodiff: autodiff_items.clone(),
|
||||
|
|
@ -1495,9 +1477,13 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
dcx.handle().emit_fatal(AutodiffWithoutLto {});
|
||||
}
|
||||
|
||||
for (work, cost) in
|
||||
generate_thin_lto_work(&cgcx, needs_thin_lto, import_only_modules)
|
||||
{
|
||||
for (work, cost) in generate_thin_lto_work(
|
||||
&cgcx,
|
||||
&exported_symbols_for_lto,
|
||||
&each_linked_rlib_file_for_lto,
|
||||
needs_thin_lto,
|
||||
import_only_modules,
|
||||
) {
|
||||
let insertion_index = work_items
|
||||
.binary_search_by_key(&cost, |&(_, cost)| cost)
|
||||
.unwrap_or_else(|e| e);
|
||||
|
|
@ -1516,12 +1502,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
MainThreadState::Idle => {
|
||||
if let Some((item, _)) = work_items.pop() {
|
||||
main_thread_state = MainThreadState::Lending;
|
||||
spawn_work(
|
||||
&cgcx,
|
||||
&mut llvm_start_time,
|
||||
get_worker_id(&mut free_worker_ids),
|
||||
item,
|
||||
);
|
||||
spawn_work(&cgcx, &mut llvm_start_time, item);
|
||||
} else {
|
||||
// There is no unstarted work, so let the main thread
|
||||
// take over for a running worker. Otherwise the
|
||||
|
|
@ -1557,12 +1538,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
while running_with_own_token < tokens.len()
|
||||
&& let Some((item, _)) = work_items.pop()
|
||||
{
|
||||
spawn_work(
|
||||
&cgcx,
|
||||
&mut llvm_start_time,
|
||||
get_worker_id(&mut free_worker_ids),
|
||||
item,
|
||||
);
|
||||
spawn_work(&cgcx, &mut llvm_start_time, item);
|
||||
running_with_own_token += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1570,21 +1546,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
// Relinquish accidentally acquired extra tokens.
|
||||
tokens.truncate(running_with_own_token);
|
||||
|
||||
// If a thread exits successfully then we drop a token associated
|
||||
// with that worker and update our `running_with_own_token` count.
|
||||
// We may later re-acquire a token to continue running more work.
|
||||
// We may also not actually drop a token here if the worker was
|
||||
// running with an "ephemeral token".
|
||||
let mut free_worker = |worker_id| {
|
||||
if main_thread_state == MainThreadState::Lending {
|
||||
main_thread_state = MainThreadState::Idle;
|
||||
} else {
|
||||
running_with_own_token -= 1;
|
||||
}
|
||||
|
||||
free_worker_ids.push(worker_id);
|
||||
};
|
||||
|
||||
let msg = coordinator_receive.recv().unwrap();
|
||||
match *msg.downcast::<Message<B>>().ok().unwrap() {
|
||||
// Save the token locally and the next turn of the loop will use
|
||||
|
|
@ -1653,8 +1614,17 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
codegen_state = Aborted;
|
||||
}
|
||||
|
||||
Message::WorkItem { result, worker_id } => {
|
||||
free_worker(worker_id);
|
||||
Message::WorkItem { result } => {
|
||||
// If a thread exits successfully then we drop a token associated
|
||||
// with that worker and update our `running_with_own_token` count.
|
||||
// We may later re-acquire a token to continue running more work.
|
||||
// We may also not actually drop a token here if the worker was
|
||||
// running with an "ephemeral token".
|
||||
if main_thread_state == MainThreadState::Lending {
|
||||
main_thread_state = MainThreadState::Idle;
|
||||
} else {
|
||||
running_with_own_token -= 1;
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(WorkItemResult::Finished(compiled_module)) => {
|
||||
|
|
@ -1800,7 +1770,6 @@ pub(crate) struct WorkerFatalError;
|
|||
fn spawn_work<'a, B: ExtraBackendMethods>(
|
||||
cgcx: &'a CodegenContext<B>,
|
||||
llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
|
||||
worker_id: usize,
|
||||
work: WorkItem<B>,
|
||||
) {
|
||||
if cgcx.config(work.module_kind()).time_module && llvm_start_time.is_none() {
|
||||
|
|
@ -1815,24 +1784,21 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
struct Bomb<B: ExtraBackendMethods> {
|
||||
coordinator_send: Sender<Box<dyn Any + Send>>,
|
||||
result: Option<Result<WorkItemResult<B>, FatalError>>,
|
||||
worker_id: usize,
|
||||
}
|
||||
impl<B: ExtraBackendMethods> Drop for Bomb<B> {
|
||||
fn drop(&mut self) {
|
||||
let worker_id = self.worker_id;
|
||||
let msg = match self.result.take() {
|
||||
Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result), worker_id },
|
||||
Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result) },
|
||||
Some(Err(FatalError)) => {
|
||||
Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)), worker_id }
|
||||
Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)) }
|
||||
}
|
||||
None => Message::WorkItem::<B> { result: Err(None), worker_id },
|
||||
None => Message::WorkItem::<B> { result: Err(None) },
|
||||
};
|
||||
drop(self.coordinator_send.send(Box::new(msg)));
|
||||
}
|
||||
}
|
||||
|
||||
let mut bomb =
|
||||
Bomb::<B> { coordinator_send: cgcx.coordinator_send.clone(), result: None, worker_id };
|
||||
let mut bomb = Bomb::<B> { coordinator_send: cgcx.coordinator_send.clone(), result: None };
|
||||
|
||||
// Execute the work itself, and if it finishes successfully then flag
|
||||
// ourselves as a success as well.
|
||||
|
|
@ -1856,12 +1822,20 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
);
|
||||
Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config))
|
||||
}
|
||||
WorkItem::FatLto { needs_fat_lto, import_only_modules, autodiff } => {
|
||||
WorkItem::FatLto {
|
||||
exported_symbols_for_lto,
|
||||
each_linked_rlib_for_lto,
|
||||
needs_fat_lto,
|
||||
import_only_modules,
|
||||
autodiff,
|
||||
} => {
|
||||
let _timer = cgcx
|
||||
.prof
|
||||
.generic_activity_with_arg("codegen_module_perform_lto", "everything");
|
||||
execute_fat_lto_work_item(
|
||||
&cgcx,
|
||||
&exported_symbols_for_lto,
|
||||
&each_linked_rlib_for_lto,
|
||||
needs_fat_lto,
|
||||
import_only_modules,
|
||||
autodiff,
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
pub fn asm_const_to_str<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sp: Span,
|
||||
const_value: mir::ConstValue<'tcx>,
|
||||
const_value: mir::ConstValue,
|
||||
ty_and_layout: TyAndLayout<'tcx>,
|
||||
) -> String {
|
||||
let mir::ConstValue::Scalar(scalar) = const_value else {
|
||||
|
|
|
|||
|
|
@ -1294,3 +1294,20 @@ pub(crate) struct FeatureNotValid<'a> {
|
|||
#[help]
|
||||
pub plus_hint: bool,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_lto_disallowed)]
|
||||
pub(crate) struct LtoDisallowed;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_lto_dylib)]
|
||||
pub(crate) struct LtoDylib;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_lto_proc_macro)]
|
||||
pub(crate) struct LtoProcMacro;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_dynamic_linking_with_lto)]
|
||||
#[note]
|
||||
pub(crate) struct DynamicLinkingWithLTO;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
OperandRef::from_const(bx, val, ty)
|
||||
}
|
||||
|
||||
pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue<'tcx> {
|
||||
pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue {
|
||||
// `MirUsedCollector` visited all required_consts before codegen began, so if we got here
|
||||
// there can be no more constants that fail to evaluate.
|
||||
self.monomorphize(constant.const_)
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
|
||||
pub(crate) fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||
bx: &mut Bx,
|
||||
val: mir::ConstValue<'tcx>,
|
||||
val: mir::ConstValue,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Self {
|
||||
let layout = bx.layout_of(ty);
|
||||
|
|
@ -154,14 +154,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
OperandValue::Immediate(llval)
|
||||
}
|
||||
ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
|
||||
ConstValue::Slice { data, meta } => {
|
||||
ConstValue::Slice { alloc_id, meta } => {
|
||||
let BackendRepr::ScalarPair(a_scalar, _) = layout.backend_repr else {
|
||||
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
|
||||
};
|
||||
let a = Scalar::from_pointer(
|
||||
Pointer::new(bx.tcx().reserve_and_set_memory_alloc(data).into(), Size::ZERO),
|
||||
&bx.tcx(),
|
||||
);
|
||||
let a = Scalar::from_pointer(Pointer::new(alloc_id.into(), Size::ZERO), &bx.tcx());
|
||||
let a_llval = bx.scalar_to_backend(
|
||||
a,
|
||||
a_scalar,
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
// valid ranges. For example, `char`s are passed as just `i32`, with no
|
||||
// way for LLVM to know that they're 0x10FFFF at most. Thus we assume
|
||||
// the range of the input value too, not just the output range.
|
||||
assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
|
||||
assume_scalar_range(bx, imm, from_scalar, from_backend_ty, None);
|
||||
|
||||
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
|
||||
(Int(_, is_signed), Int(..)) => bx.intcast(imm, to_backend_ty, is_signed),
|
||||
|
|
@ -869,7 +869,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let ltext = bx.zext(is_lt, bx.type_i8());
|
||||
bx.unchecked_ssub(gtext, ltext)
|
||||
} else {
|
||||
// These operations are those expected by `tests/codegen/integer-cmp.rs`,
|
||||
// These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`,
|
||||
// from <https://github.com/rust-lang/rust/pull/63767>.
|
||||
let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs);
|
||||
let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs);
|
||||
|
|
@ -1064,7 +1064,7 @@ pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
// That said, last time we tried removing this, it didn't actually help
|
||||
// the rustc-perf results, so might as well keep doing it
|
||||
// <https://github.com/rust-lang/rust/pull/135610#issuecomment-2599275182>
|
||||
assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
|
||||
assume_scalar_range(bx, imm, from_scalar, from_backend_ty, Some(&to_scalar));
|
||||
|
||||
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
|
||||
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
|
||||
|
|
@ -1092,22 +1092,42 @@ pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
// since it's never passed to something with parameter metadata (especially
|
||||
// after MIR inlining) so the only way to tell the backend about the
|
||||
// constraint that the `transmute` introduced is to `assume` it.
|
||||
assume_scalar_range(bx, imm, to_scalar, to_backend_ty);
|
||||
assume_scalar_range(bx, imm, to_scalar, to_backend_ty, Some(&from_scalar));
|
||||
|
||||
imm = bx.to_immediate_scalar(imm, to_scalar);
|
||||
imm
|
||||
}
|
||||
|
||||
/// Emits an `assume` call that `imm`'s value is within the known range of `scalar`.
|
||||
///
|
||||
/// If `known` is `Some`, only emits the assume if it's more specific than
|
||||
/// whatever is already known from the range of *that* scalar.
|
||||
fn assume_scalar_range<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
bx: &mut Bx,
|
||||
imm: Bx::Value,
|
||||
scalar: abi::Scalar,
|
||||
backend_ty: Bx::Type,
|
||||
known: Option<&abi::Scalar>,
|
||||
) {
|
||||
if matches!(bx.cx().sess().opts.optimize, OptLevel::No) || scalar.is_always_valid(bx.cx()) {
|
||||
if matches!(bx.cx().sess().opts.optimize, OptLevel::No) {
|
||||
return;
|
||||
}
|
||||
|
||||
match (scalar, known) {
|
||||
(abi::Scalar::Union { .. }, _) => return,
|
||||
(_, None) => {
|
||||
if scalar.is_always_valid(bx.cx()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
(abi::Scalar::Initialized { valid_range, .. }, Some(known)) => {
|
||||
let known_range = known.valid_range(bx.cx());
|
||||
if valid_range.contains_range(known_range, scalar.size(bx.cx())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match scalar.primitive() {
|
||||
abi::Primitive::Int(..) => {
|
||||
let range = scalar.valid_range(bx.cx());
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
|
||||
use rustc_errors::{DiagCtxtHandle, FatalError};
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
|
|
@ -24,8 +26,9 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
/// if necessary and running any further optimizations
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<FatLtoInput<Self>>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError>;
|
||||
/// Performs thin LTO by performing necessary global analysis and returning two
|
||||
|
|
@ -33,6 +36,8 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
/// can simply be copied over from the incr. comp. cache.
|
||||
fn run_thin_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
each_linked_rlib_for_lto: &[PathBuf],
|
||||
modules: Vec<(String, Self::ThinBuffer)>,
|
||||
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
||||
) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError>;
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>(
|
|||
pub fn mk_eval_cx_for_const_val<'tcx>(
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
val: mir::ConstValue<'tcx>,
|
||||
val: mir::ConstValue,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> {
|
||||
let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, typing_env, CanAccessMutGlobal::No);
|
||||
|
|
@ -172,7 +172,7 @@ pub(super) fn op_to_const<'tcx>(
|
|||
ecx: &CompileTimeInterpCx<'tcx>,
|
||||
op: &OpTy<'tcx>,
|
||||
for_diagnostics: bool,
|
||||
) -> ConstValue<'tcx> {
|
||||
) -> ConstValue {
|
||||
// Handle ZST consistently and early.
|
||||
if op.layout.is_zst() {
|
||||
return ConstValue::ZeroSized;
|
||||
|
|
@ -241,10 +241,9 @@ pub(super) fn op_to_const<'tcx>(
|
|||
let (prov, offset) =
|
||||
ptr.into_pointer_or_addr().expect(msg).prov_and_relative_offset();
|
||||
let alloc_id = prov.alloc_id();
|
||||
let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
|
||||
assert!(offset == abi::Size::ZERO, "{}", msg);
|
||||
let meta = b.to_target_usize(ecx).expect(msg);
|
||||
ConstValue::Slice { data, meta }
|
||||
ConstValue::Slice { alloc_id, meta }
|
||||
}
|
||||
Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
|
||||
},
|
||||
|
|
@ -256,7 +255,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
constant: ConstAlloc<'tcx>,
|
||||
key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
|
||||
) -> ConstValue<'tcx> {
|
||||
) -> ConstValue {
|
||||
let cid = key.value;
|
||||
let def_id = cid.instance.def.def_id();
|
||||
let is_static = tcx.is_static(def_id);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const VALTREE_MAX_NODES: usize = 100000;
|
|||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
val: mir::ConstValue<'tcx>,
|
||||
val: mir::ConstValue,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<mir::DestructuredConstant<'tcx>> {
|
||||
let typing_env = ty::TypingEnv::fully_monomorphized();
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ pub fn valtree_to_const_value<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
cv: ty::Value<'tcx>,
|
||||
) -> mir::ConstValue<'tcx> {
|
||||
) -> mir::ConstValue {
|
||||
// Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
|
||||
// (those for constants with type bool, int, uint, float or char).
|
||||
// For all other types we create an `MPlace` and fill that by walking
|
||||
|
|
|
|||
|
|
@ -582,8 +582,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
span: Span,
|
||||
layout: Option<TyAndLayout<'tcx>>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
|
||||
let const_val = val.eval(*ecx.tcx, ecx.typing_env, span).map_err(|err| {
|
||||
let const_val = val.eval(*self.tcx, self.typing_env, span).map_err(|err| {
|
||||
if M::ALL_CONSTS_ARE_PRECHECKED {
|
||||
match err {
|
||||
ErrorHandled::TooGeneric(..) => {},
|
||||
|
|
@ -599,11 +598,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
}
|
||||
err.emit_note(*ecx.tcx);
|
||||
err.emit_note(*self.tcx);
|
||||
err
|
||||
})?;
|
||||
ecx.const_val_to_op(const_val, val.ty(), layout)
|
||||
})
|
||||
self.const_val_to_op(const_val, val.ty(), layout)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::assert_matches::assert_matches;
|
|||
|
||||
use rustc_abi::{FieldIdx, HasDataLayout, Size};
|
||||
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
|
||||
use rustc_middle::mir::interpret::{read_target_uint, write_target_uint};
|
||||
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
|
||||
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
|
|
@ -17,17 +17,18 @@ use tracing::trace;
|
|||
use super::memory::MemoryKind;
|
||||
use super::util::ensure_monomorphic_enough;
|
||||
use super::{
|
||||
Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy,
|
||||
PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format,
|
||||
interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
|
||||
AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer,
|
||||
PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, throw_inval,
|
||||
throw_ub_custom, throw_ub_format,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
|
||||
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> {
|
||||
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
|
||||
let path = crate::util::type_name(tcx, ty);
|
||||
let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes(), ());
|
||||
tcx.mk_const_alloc(alloc)
|
||||
let bytes = path.into_bytes();
|
||||
let len = bytes.len().try_into().unwrap();
|
||||
(tcx.allocate_bytes_dedup(bytes, CTFE_ALLOC_SALT), len)
|
||||
}
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Generates a value of `TypeId` for `ty` in-place.
|
||||
|
|
@ -126,8 +127,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
sym::type_name => {
|
||||
let tp_ty = instance.args.type_at(0);
|
||||
ensure_monomorphic_enough(tcx, tp_ty)?;
|
||||
let alloc = alloc_type_name(tcx, tp_ty);
|
||||
let val = ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() };
|
||||
let (alloc_id, meta) = alloc_type_name(tcx, tp_ty);
|
||||
let val = ConstValue::Slice { alloc_id, meta };
|
||||
let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?;
|
||||
self.copy_op(&val, dest)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ use rustc_middle::query::TyCtxtAt;
|
|||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::callconv::FnAbi;
|
||||
|
||||
|
|
@ -587,27 +586,6 @@ pub trait Machine<'tcx>: Sized {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Evaluate the given constant. The `eval` function will do all the required evaluation,
|
||||
/// but this hook has the chance to do some pre/postprocessing.
|
||||
#[inline(always)]
|
||||
fn eval_mir_constant<F>(
|
||||
ecx: &InterpCx<'tcx, Self>,
|
||||
val: mir::Const<'tcx>,
|
||||
span: Span,
|
||||
layout: Option<TyAndLayout<'tcx>>,
|
||||
eval: F,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>
|
||||
where
|
||||
F: Fn(
|
||||
&InterpCx<'tcx, Self>,
|
||||
mir::Const<'tcx>,
|
||||
Span,
|
||||
Option<TyAndLayout<'tcx>>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>,
|
||||
{
|
||||
eval(ecx, val, span, layout)
|
||||
}
|
||||
|
||||
/// Returns the salt to be used for a deduplicated global alloation.
|
||||
/// If the allocation is for a function, the instance is provided as well
|
||||
/// (this lets Miri ensure unique addresses for some functions).
|
||||
|
|
|
|||
|
|
@ -1000,7 +1000,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
ptr: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, (Ty<'tcx>, u64)> {
|
||||
let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else {
|
||||
let Some(GlobalAlloc::TypeId { ty }) = self.tcx.try_get_global_alloc(alloc_id) else {
|
||||
throw_ub_format!("invalid `TypeId` value: not all bytes carry type id metadata")
|
||||
};
|
||||
interp_ok((ty, offset.bytes()))
|
||||
|
|
|
|||
|
|
@ -836,7 +836,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
pub(crate) fn const_val_to_op(
|
||||
&self,
|
||||
val_val: mir::ConstValue<'tcx>,
|
||||
val_val: mir::ConstValue,
|
||||
ty: Ty<'tcx>,
|
||||
layout: Option<TyAndLayout<'tcx>>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
|
|
@ -860,9 +860,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
|
||||
mir::ConstValue::ZeroSized => Immediate::Uninit,
|
||||
mir::ConstValue::Slice { data, meta } => {
|
||||
mir::ConstValue::Slice { alloc_id, meta } => {
|
||||
// This is const data, no mutation allowed.
|
||||
let alloc_id = self.tcx.reserve_and_set_memory_alloc(data);
|
||||
let ptr = Pointer::new(CtfeProvenance::from(alloc_id).as_immutable(), Size::ZERO);
|
||||
Immediate::new_slice(self.global_root_pointer(ptr)?.into(), meta, self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ pub(crate) fn const_caller_location_provider(
|
|||
file: Symbol,
|
||||
line: u32,
|
||||
col: u32,
|
||||
) -> mir::ConstValue<'_> {
|
||||
) -> mir::ConstValue {
|
||||
trace!("const_caller_location: {}:{}:{}", file, line, col);
|
||||
let mut ecx = mk_eval_cx_to_read_const_val(
|
||||
tcx,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
Macro import declaration was malformed.
|
||||
|
||||
Erroneous code examples:
|
||||
|
||||
```compile_fail,E0466
|
||||
```compile_fail
|
||||
#[macro_use(a_macro(another_macro))] // error: invalid import declaration
|
||||
extern crate core as some_crate;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::default::Default;
|
||||
use std::iter;
|
||||
use std::path::Component::Prefix;
|
||||
|
|
@ -361,17 +362,13 @@ where
|
|||
}
|
||||
|
||||
/// Represents a thing that maps token trees to Macro Results
|
||||
pub trait TTMacroExpander {
|
||||
pub trait TTMacroExpander: Any {
|
||||
fn expand<'cx>(
|
||||
&self,
|
||||
ecx: &'cx mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
input: TokenStream,
|
||||
) -> MacroExpanderResult<'cx>;
|
||||
|
||||
fn get_unused_rule(&self, _rule_i: usize) -> Option<(&Ident, Span)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>;
|
||||
|
|
@ -379,7 +376,7 @@ pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>;
|
|||
pub type MacroExpanderFn =
|
||||
for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>;
|
||||
|
||||
impl<F> TTMacroExpander for F
|
||||
impl<F: 'static> TTMacroExpander for F
|
||||
where
|
||||
F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ mod placeholders;
|
|||
mod proc_macro_server;
|
||||
mod stats;
|
||||
|
||||
pub use mbe::macro_rules::compile_declarative_macro;
|
||||
pub use mbe::macro_rules::{MacroRulesMacroExpander, compile_declarative_macro};
|
||||
pub mod base;
|
||||
pub mod config;
|
||||
pub mod expand;
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ pub(super) struct MacroRule {
|
|||
rhs: mbe::TokenTree,
|
||||
}
|
||||
|
||||
struct MacroRulesMacroExpander {
|
||||
pub struct MacroRulesMacroExpander {
|
||||
node_id: NodeId,
|
||||
name: Ident,
|
||||
span: Span,
|
||||
|
|
@ -136,6 +136,14 @@ struct MacroRulesMacroExpander {
|
|||
rules: Vec<MacroRule>,
|
||||
}
|
||||
|
||||
impl MacroRulesMacroExpander {
|
||||
pub fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, Span)> {
|
||||
// If the rhs contains an invocation like `compile_error!`, don't report it as unused.
|
||||
let rule = &self.rules[rule_i];
|
||||
if has_compile_error_macro(&rule.rhs) { None } else { Some((&self.name, rule.lhs_span)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl TTMacroExpander for MacroRulesMacroExpander {
|
||||
fn expand<'cx>(
|
||||
&self,
|
||||
|
|
@ -154,12 +162,6 @@ impl TTMacroExpander for MacroRulesMacroExpander {
|
|||
&self.rules,
|
||||
))
|
||||
}
|
||||
|
||||
fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, Span)> {
|
||||
// If the rhs contains an invocation like `compile_error!`, don't report it as unused.
|
||||
let rule = &self.rules[rule_i];
|
||||
if has_compile_error_macro(&rule.rhs) { None } else { Some((&self.name, rule.lhs_span)) }
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyExpander(ErrorGuaranteed);
|
||||
|
|
|
|||
|
|
@ -1302,6 +1302,7 @@ impl AttributeExt for Attribute {
|
|||
// FIXME: should not be needed anymore when all attrs are parsed
|
||||
Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
|
||||
Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span,
|
||||
|
|
|
|||
|
|
@ -1642,20 +1642,40 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||
|
||||
if def.repr().int.is_none() {
|
||||
let is_unit = |var: &ty::VariantDef| matches!(var.ctor_kind(), Some(CtorKind::Const));
|
||||
let has_disr = |var: &ty::VariantDef| matches!(var.discr, ty::VariantDiscr::Explicit(_));
|
||||
let get_disr = |var: &ty::VariantDef| match var.discr {
|
||||
ty::VariantDiscr::Explicit(disr) => Some(disr),
|
||||
ty::VariantDiscr::Relative(_) => None,
|
||||
};
|
||||
|
||||
let has_non_units = def.variants().iter().any(|var| !is_unit(var));
|
||||
let disr_units = def.variants().iter().any(|var| is_unit(var) && has_disr(var));
|
||||
let disr_non_unit = def.variants().iter().any(|var| !is_unit(var) && has_disr(var));
|
||||
let non_unit = def.variants().iter().find(|var| !is_unit(var));
|
||||
let disr_unit =
|
||||
def.variants().iter().filter(|var| is_unit(var)).find_map(|var| get_disr(var));
|
||||
let disr_non_unit =
|
||||
def.variants().iter().filter(|var| !is_unit(var)).find_map(|var| get_disr(var));
|
||||
|
||||
if disr_non_unit || (disr_units && has_non_units) {
|
||||
struct_span_code_err!(
|
||||
if disr_non_unit.is_some() || (disr_unit.is_some() && non_unit.is_some()) {
|
||||
let mut err = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
tcx.def_span(def_id),
|
||||
E0732,
|
||||
"`#[repr(inttype)]` must be specified"
|
||||
)
|
||||
.emit();
|
||||
"`#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants"
|
||||
);
|
||||
if let Some(disr_non_unit) = disr_non_unit {
|
||||
err.span_label(
|
||||
tcx.def_span(disr_non_unit),
|
||||
"explicit discriminant on non-unit variant specified here",
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
tcx.def_span(disr_unit.unwrap()),
|
||||
"explicit discriminant specified here",
|
||||
);
|
||||
err.span_label(
|
||||
tcx.def_span(non_unit.unwrap().def_id),
|
||||
"non-unit discriminant declared here",
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) {
|
|||
used_trait_imports.extend_unord(imports.items().copied());
|
||||
}
|
||||
|
||||
for &id in tcx.maybe_unused_trait_imports(()) {
|
||||
for &id in tcx.resolutions(()).maybe_unused_trait_imports.iter() {
|
||||
debug_assert_eq!(tcx.def_kind(id), DefKind::Use);
|
||||
if tcx.visibility(id).is_public() {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -447,17 +447,30 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) {
|
||||
let mut parents = self.tcx().hir_parent_iter(self_ty.hir_id);
|
||||
|
||||
if let Some((_, hir::Node::AssocItemConstraint(constraint))) = parents.next()
|
||||
if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next()
|
||||
&& let Some(obj_ty) = constraint.ty()
|
||||
&& let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next()
|
||||
{
|
||||
if let Some((_, hir::Node::TraitRef(..))) = parents.next()
|
||||
&& let Some((_, hir::Node::Ty(ty))) = parents.next()
|
||||
if let Some((_, hir::Node::Ty(ty))) = parents.next()
|
||||
&& let hir::TyKind::TraitObject(..) = ty.kind
|
||||
{
|
||||
// Assoc ty bounds aren't permitted inside trait object types.
|
||||
return;
|
||||
}
|
||||
|
||||
if trait_ref
|
||||
.path
|
||||
.segments
|
||||
.iter()
|
||||
.find_map(|seg| {
|
||||
seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id))
|
||||
})
|
||||
.is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No)
|
||||
{
|
||||
// Only consider angle-bracketed args (where we have a `=` to replace with `:`).
|
||||
return;
|
||||
}
|
||||
|
||||
let lo = if constraint.gen_args.span_ext.is_dummy() {
|
||||
constraint.ident.span
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1302,8 +1302,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
None => ".clone()".to_string(),
|
||||
};
|
||||
|
||||
let span = expr.span.find_oldest_ancestor_in_same_ctxt().shrink_to_hi();
|
||||
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span.shrink_to_hi(),
|
||||
span,
|
||||
"consider using clone here",
|
||||
suggestion,
|
||||
Applicability::MachineApplicable,
|
||||
|
|
|
|||
|
|
@ -1047,7 +1047,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
|
||||
lint.note("for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
|
||||
|
||||
let diagnostic_msg = format!(
|
||||
"add a dummy let to cause {migrated_variables_concat} to be fully captured"
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ lint_non_camel_case_type = {$sort} `{$name}` should have an upper camel case nam
|
|||
|
||||
lint_non_fmt_panic = panic message is not a string literal
|
||||
.note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021
|
||||
.more_info_note = for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
|
||||
.more_info_note = for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/panic-macro-consistency.html>
|
||||
.supports_fmt_note = the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here
|
||||
.supports_fmt_suggestion = remove the `format!(..)` macro call
|
||||
.display_suggestion = add a "{"{"}{"}"}" format string to `Display` the message
|
||||
|
|
|
|||
|
|
@ -1654,7 +1654,7 @@ declare_lint! {
|
|||
"`...` range patterns are deprecated",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/warnings-promoted-to-error.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1835,7 +1835,7 @@ declare_lint! {
|
|||
"detects edition keywords being used as an identifier",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/gen-keyword.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/gen-keyword.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ declare_lint! {
|
|||
rewriting in `match` is an option to preserve the semantics up to Edition 2021",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ declare_lint! {
|
|||
"`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rpit-lifetime-capture.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/rpit-lifetime-capture.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ declare_lint! {
|
|||
/// to ensure the macros implement the desired behavior.
|
||||
///
|
||||
/// [editions]: https://doc.rust-lang.org/edition-guide/
|
||||
/// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html
|
||||
/// [macro matcher fragment specifiers]: https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html
|
||||
/// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
|
||||
pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
|
||||
Allow,
|
||||
|
|
@ -73,7 +73,7 @@ declare_lint! {
|
|||
To keep the existing behavior, use the `expr_2021` fragment specifier.",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>",
|
||||
reference: "Migration Guide <https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ declare_lint! {
|
|||
"detects calling `into_iter` on arrays in Rust 2015 and 2018",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ declare_lint! {
|
|||
"detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/intoiterator-box-slice.html>"
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/intoiterator-box-slice.html>"
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ declare_lint! {
|
|||
"creating a shared reference to mutable static",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>",
|
||||
explain_reason: false,
|
||||
};
|
||||
@edition Edition2024 => Deny;
|
||||
|
|
|
|||
|
|
@ -1814,7 +1814,7 @@ declare_lint! {
|
|||
"suggest using `dyn Trait` for trait objects",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/warnings-promoted-to-error.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2472,7 +2472,7 @@ declare_lint! {
|
|||
"unsafe operations in unsafe functions without an explicit unsafe block are deprecated",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>",
|
||||
explain_reason: false
|
||||
};
|
||||
@edition Edition2024 => Warn;
|
||||
|
|
@ -3445,7 +3445,7 @@ declare_lint! {
|
|||
"detects usage of old versions of or-patterns",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/or-patterns-macro-rules.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -3494,7 +3494,7 @@ declare_lint! {
|
|||
prelude in future editions",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/prelude.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/prelude.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -3534,7 +3534,7 @@ declare_lint! {
|
|||
prelude in future editions",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/prelude.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/prelude.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -3571,7 +3571,7 @@ declare_lint! {
|
|||
"identifiers that will be parsed as a prefix in Rust 2021",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/reserving-syntax.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>",
|
||||
};
|
||||
crate_level_only
|
||||
}
|
||||
|
|
@ -4100,7 +4100,7 @@ declare_lint! {
|
|||
"never type fallback affecting unsafe function calls",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/never-type-fallback.html>",
|
||||
report_in_deps: true,
|
||||
};
|
||||
@edition Edition2024 => Deny;
|
||||
|
|
@ -4155,7 +4155,7 @@ declare_lint! {
|
|||
"never type fallback affecting unsafe function calls",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionAndFutureReleaseError(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/never-type-fallback.html>",
|
||||
report_in_deps: true,
|
||||
};
|
||||
report_in_external_macro
|
||||
|
|
@ -4740,7 +4740,7 @@ declare_lint! {
|
|||
"detects unsafe functions being used as safe functions",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/newly-unsafe-functions.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/newly-unsafe-functions.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -4776,7 +4776,7 @@ declare_lint! {
|
|||
"detects missing unsafe keyword on extern declarations",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-extern.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-extern.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -4817,7 +4817,7 @@ declare_lint! {
|
|||
"detects unsafe attributes outside of unsafe",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-attributes.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-attributes.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -5014,7 +5014,7 @@ declare_lint! {
|
|||
"Detect and warn on significant change in drop order in tail expression location",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/temporary-tail-expr-scope.html>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -5053,7 +5053,7 @@ declare_lint! {
|
|||
"will be parsed as a guarded string in Rust 2024",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
|
||||
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/reserved-syntax.html>",
|
||||
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/reserved-syntax.html>",
|
||||
};
|
||||
crate_level_only
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ mod symbols;
|
|||
mod try_from;
|
||||
mod type_foldable;
|
||||
mod type_visitable;
|
||||
mod visitable;
|
||||
|
||||
// Reads the rust version (e.g. "1.75.0") from the CFG_RELEASE env var and
|
||||
// produces a `RustcVersion` literal containing that version (e.g.
|
||||
|
|
@ -101,6 +102,16 @@ decl_derive!(
|
|||
/// visited (and its type is not required to implement `TypeVisitable`).
|
||||
type_visitable::type_visitable_derive
|
||||
);
|
||||
decl_derive!(
|
||||
[Walkable, attributes(visitable)] =>
|
||||
/// Derives `Walkable` for the annotated `struct` or `enum` (`union` is not supported).
|
||||
///
|
||||
/// Each field of the struct or enum variant will be visited in definition order, using the
|
||||
/// `Walkable` implementation for its type. However, if a field of a struct or an enum
|
||||
/// variant is annotated with `#[visitable(ignore)]` then that field will not be
|
||||
/// visited (and its type is not required to implement `Walkable`).
|
||||
visitable::visitable_derive
|
||||
);
|
||||
decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
|
||||
decl_derive!(
|
||||
[Diagnostic, attributes(
|
||||
|
|
|
|||
82
compiler/rustc_macros/src/visitable.rs
Normal file
82
compiler/rustc_macros/src/visitable.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use quote::quote;
|
||||
use synstructure::BindingInfo;
|
||||
|
||||
pub(super) fn visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
|
||||
if let syn::Data::Union(_) = s.ast().data {
|
||||
panic!("cannot derive on union")
|
||||
}
|
||||
|
||||
let has_attr = |bind: &BindingInfo<'_>, name| {
|
||||
let mut found = false;
|
||||
bind.ast().attrs.iter().for_each(|attr| {
|
||||
if !attr.path().is_ident("visitable") {
|
||||
return;
|
||||
}
|
||||
let _ = attr.parse_nested_meta(|nested| {
|
||||
if nested.path.is_ident(name) {
|
||||
found = true;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
});
|
||||
found
|
||||
};
|
||||
|
||||
let get_attr = |bind: &BindingInfo<'_>, name: &str| {
|
||||
let mut content = None;
|
||||
bind.ast().attrs.iter().for_each(|attr| {
|
||||
if !attr.path().is_ident("visitable") {
|
||||
return;
|
||||
}
|
||||
let _ = attr.parse_nested_meta(|nested| {
|
||||
if nested.path.is_ident(name) {
|
||||
let value = nested.value()?;
|
||||
let value = value.parse()?;
|
||||
content = Some(value);
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
});
|
||||
content
|
||||
};
|
||||
|
||||
s.add_bounds(synstructure::AddBounds::Generics);
|
||||
s.bind_with(|_| synstructure::BindStyle::Ref);
|
||||
let ref_visit = s.each(|bind| {
|
||||
let extra = get_attr(bind, "extra").unwrap_or(quote! {});
|
||||
if has_attr(bind, "ignore") {
|
||||
quote! {}
|
||||
} else {
|
||||
quote! { rustc_ast_ir::try_visit!(crate::visit::Visitable::visit(#bind, __visitor, (#extra))) }
|
||||
}
|
||||
});
|
||||
|
||||
s.bind_with(|_| synstructure::BindStyle::RefMut);
|
||||
let mut_visit = s.each(|bind| {
|
||||
let extra = get_attr(bind, "extra").unwrap_or(quote! {});
|
||||
if has_attr(bind, "ignore") {
|
||||
quote! {}
|
||||
} else {
|
||||
quote! { crate::mut_visit::MutVisitable::visit_mut(#bind, __visitor, (#extra)) }
|
||||
}
|
||||
});
|
||||
|
||||
s.gen_impl(quote! {
|
||||
gen impl<'__ast, __V> crate::visit::Walkable<'__ast, __V> for @Self
|
||||
where __V: crate::visit::Visitor<'__ast>,
|
||||
{
|
||||
fn walk_ref(&'__ast self, __visitor: &mut __V) -> __V::Result {
|
||||
match *self { #ref_visit }
|
||||
<__V::Result as rustc_ast_ir::visit::VisitorResult>::output()
|
||||
}
|
||||
}
|
||||
|
||||
gen impl<__V> crate::mut_visit::MutWalkable<__V> for @Self
|
||||
where __V: crate::mut_visit::MutVisitor,
|
||||
{
|
||||
fn walk_mut(&mut self, __visitor: &mut __V) {
|
||||
match *self { #mut_visit }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -330,3 +330,6 @@ metadata_wasm_import_form =
|
|||
|
||||
metadata_whole_archive_needs_static =
|
||||
linking modifier `whole-archive` is only compatible with `static` linking kind
|
||||
|
||||
metadata_raw_dylib_malformed =
|
||||
link name must be well-formed if link kind is `raw-dylib`
|
||||
|
|
|
|||
|
|
@ -815,3 +815,10 @@ pub struct AsyncDropTypesInDependency {
|
|||
pub extern_crate: Symbol,
|
||||
pub local_crate: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(metadata_raw_dylib_malformed)]
|
||||
pub struct RawDylibMalformed {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -700,8 +700,21 @@ impl<'tcx> Collector<'tcx> {
|
|||
.link_ordinal
|
||||
.map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
|
||||
|
||||
let name = codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item));
|
||||
|
||||
if self.tcx.sess.target.binary_format == BinaryFormat::Elf {
|
||||
let name = name.as_str();
|
||||
if name.contains('\0') {
|
||||
self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
|
||||
} else if let Some((left, right)) = name.split_once('@')
|
||||
&& (left.is_empty() || right.is_empty() || right.contains('@'))
|
||||
{
|
||||
self.tcx.dcx().emit_err(errors::RawDylibMalformed { span });
|
||||
}
|
||||
}
|
||||
|
||||
DllImport {
|
||||
name: codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item)),
|
||||
name,
|
||||
import_name_type,
|
||||
calling_convention,
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ macro_rules! declare_hooks {
|
|||
declare_hooks! {
|
||||
/// Tries to destructure an `mir::Const` ADT or array into its variant index
|
||||
/// and its field values. This should only be used for pretty printing.
|
||||
hook try_destructure_mir_constant_for_user_output(val: mir::ConstValue<'tcx>, ty: Ty<'tcx>) -> Option<mir::DestructuredConstant<'tcx>>;
|
||||
hook try_destructure_mir_constant_for_user_output(val: mir::ConstValue, ty: Ty<'tcx>) -> Option<mir::DestructuredConstant<'tcx>>;
|
||||
|
||||
/// Getting a &core::panic::Location referring to a span.
|
||||
hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>;
|
||||
hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue;
|
||||
|
||||
/// Returns `true` if this def is a function-like thing that is eligible for
|
||||
/// coverage instrumentation under `-Cinstrument-coverage`.
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol};
|
|||
use rustc_type_ir::TypeVisitableExt;
|
||||
|
||||
use super::interpret::ReportedErrorInfo;
|
||||
use crate::mir::interpret::{
|
||||
AllocId, AllocRange, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar, alloc_range,
|
||||
};
|
||||
use crate::mir::interpret::{AllocId, AllocRange, ErrorHandled, GlobalAlloc, Scalar, alloc_range};
|
||||
use crate::mir::{Promoted, pretty_print_const_value};
|
||||
use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
|
||||
use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt};
|
||||
|
|
@ -33,8 +31,8 @@ pub struct ConstAlloc<'tcx> {
|
|||
/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
|
||||
/// array length computations, enum discriminants and the pattern matching logic.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
|
||||
#[derive(HashStable, Lift)]
|
||||
pub enum ConstValue<'tcx> {
|
||||
#[derive(HashStable)]
|
||||
pub enum ConstValue {
|
||||
/// Used for types with `layout::abi::Scalar` ABI.
|
||||
///
|
||||
/// Not using the enum `Value` to encode that this must not be `Uninit`.
|
||||
|
|
@ -52,7 +50,7 @@ pub enum ConstValue<'tcx> {
|
|||
Slice {
|
||||
/// The allocation storing the slice contents.
|
||||
/// This always points to the beginning of the allocation.
|
||||
data: ConstAllocation<'tcx>,
|
||||
alloc_id: AllocId,
|
||||
/// The metadata field of the reference.
|
||||
/// This is a "target usize", so we use `u64` as in the interpreter.
|
||||
meta: u64,
|
||||
|
|
@ -75,9 +73,9 @@ pub enum ConstValue<'tcx> {
|
|||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
rustc_data_structures::static_assert_size!(ConstValue<'_>, 24);
|
||||
rustc_data_structures::static_assert_size!(ConstValue, 24);
|
||||
|
||||
impl<'tcx> ConstValue<'tcx> {
|
||||
impl ConstValue {
|
||||
#[inline]
|
||||
pub fn try_to_scalar(&self) -> Option<Scalar> {
|
||||
match *self {
|
||||
|
|
@ -98,11 +96,11 @@ impl<'tcx> ConstValue<'tcx> {
|
|||
self.try_to_scalar_int()?.try_into().ok()
|
||||
}
|
||||
|
||||
pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
|
||||
pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Option<u64> {
|
||||
Some(self.try_to_scalar_int()?.to_target_usize(tcx))
|
||||
}
|
||||
|
||||
pub fn try_to_bits_for_ty(
|
||||
pub fn try_to_bits_for_ty<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
|
|
@ -132,12 +130,15 @@ impl<'tcx> ConstValue<'tcx> {
|
|||
}
|
||||
|
||||
/// Must only be called on constants of type `&str` or `&[u8]`!
|
||||
pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
|
||||
let (data, start, end) = match self {
|
||||
pub fn try_get_slice_bytes_for_diagnostics<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Option<&'tcx [u8]> {
|
||||
let (alloc_id, start, len) = match self {
|
||||
ConstValue::Scalar(_) | ConstValue::ZeroSized => {
|
||||
bug!("`try_get_slice_bytes` on non-slice constant")
|
||||
}
|
||||
&ConstValue::Slice { data, meta } => (data, 0, meta),
|
||||
&ConstValue::Slice { alloc_id, meta } => (alloc_id, 0, meta),
|
||||
&ConstValue::Indirect { alloc_id, offset } => {
|
||||
// The reference itself is stored behind an indirection.
|
||||
// Load the reference, and then load the actual slice contents.
|
||||
|
|
@ -170,26 +171,29 @@ impl<'tcx> ConstValue<'tcx> {
|
|||
// Non-empty slice, must have memory. We know this is a relative pointer.
|
||||
let (inner_prov, offset) =
|
||||
ptr.into_pointer_or_addr().ok()?.prov_and_relative_offset();
|
||||
let data = tcx.global_alloc(inner_prov.alloc_id()).unwrap_memory();
|
||||
(data, offset.bytes(), offset.bytes() + len)
|
||||
(inner_prov.alloc_id(), offset.bytes(), len)
|
||||
}
|
||||
};
|
||||
|
||||
let data = tcx.global_alloc(alloc_id).unwrap_memory();
|
||||
|
||||
// This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
|
||||
let start = start.try_into().unwrap();
|
||||
let end = end.try_into().unwrap();
|
||||
let end = start + usize::try_from(len).unwrap();
|
||||
Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
|
||||
}
|
||||
|
||||
/// Check if a constant may contain provenance information. This is used by MIR opts.
|
||||
/// Can return `true` even if there is no provenance.
|
||||
pub fn may_have_provenance(&self, tcx: TyCtxt<'tcx>, size: Size) -> bool {
|
||||
pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool {
|
||||
match *self {
|
||||
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
|
||||
ConstValue::Scalar(Scalar::Ptr(..)) => return true,
|
||||
// It's hard to find out the part of the allocation we point to;
|
||||
// just conservatively check everything.
|
||||
ConstValue::Slice { data, meta: _ } => !data.inner().provenance().ptrs().is_empty(),
|
||||
ConstValue::Slice { alloc_id, meta: _ } => {
|
||||
!tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty()
|
||||
}
|
||||
ConstValue::Indirect { alloc_id, offset } => !tcx
|
||||
.global_alloc(alloc_id)
|
||||
.unwrap_memory()
|
||||
|
|
@ -200,7 +204,7 @@ impl<'tcx> ConstValue<'tcx> {
|
|||
}
|
||||
|
||||
/// Check if a constant only contains uninitialized bytes.
|
||||
pub fn all_bytes_uninit(&self, tcx: TyCtxt<'tcx>) -> bool {
|
||||
pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool {
|
||||
let ConstValue::Indirect { alloc_id, .. } = self else {
|
||||
return false;
|
||||
};
|
||||
|
|
@ -247,7 +251,7 @@ pub enum Const<'tcx> {
|
|||
|
||||
/// This constant cannot go back into the type system, as it represents
|
||||
/// something the type system cannot handle (e.g. pointers).
|
||||
Val(ConstValue<'tcx>, Ty<'tcx>),
|
||||
Val(ConstValue, Ty<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> Const<'tcx> {
|
||||
|
|
@ -343,7 +347,7 @@ impl<'tcx> Const<'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
span: Span,
|
||||
) -> Result<ConstValue<'tcx>, ErrorHandled> {
|
||||
) -> Result<ConstValue, ErrorHandled> {
|
||||
match self {
|
||||
Const::Ty(_, c) => {
|
||||
if c.has_non_region_param() {
|
||||
|
|
@ -440,7 +444,7 @@ impl<'tcx> Const<'tcx> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
|
||||
pub fn from_value(val: ConstValue, ty: Ty<'tcx>) -> Self {
|
||||
Self::Val(val, ty)
|
||||
}
|
||||
|
||||
|
|
@ -487,9 +491,8 @@ impl<'tcx> Const<'tcx> {
|
|||
/// taking into account even pointer identity tests.
|
||||
pub fn is_deterministic(&self) -> bool {
|
||||
// Some constants may generate fresh allocations for pointers they contain,
|
||||
// so using the same constant twice can yield two different results:
|
||||
// - valtrees purposefully generate new allocations
|
||||
// - ConstValue::Slice also generate new allocations
|
||||
// so using the same constant twice can yield two different results.
|
||||
// Notably, valtrees purposefully generate new allocations.
|
||||
match self {
|
||||
Const::Ty(_, c) => match c.kind() {
|
||||
ty::ConstKind::Param(..) => true,
|
||||
|
|
@ -507,11 +510,11 @@ impl<'tcx> Const<'tcx> {
|
|||
| ty::ConstKind::Placeholder(..) => bug!(),
|
||||
},
|
||||
Const::Unevaluated(..) => false,
|
||||
// If the same slice appears twice in the MIR, we cannot guarantee that we will
|
||||
// give the same `AllocId` to the data.
|
||||
Const::Val(ConstValue::Slice { .. }, _) => false,
|
||||
Const::Val(
|
||||
ConstValue::ZeroSized | ConstValue::Scalar(_) | ConstValue::Indirect { .. },
|
||||
ConstValue::Slice { .. }
|
||||
| ConstValue::ZeroSized
|
||||
| ConstValue::Scalar(_)
|
||||
| ConstValue::Indirect { .. },
|
||||
_,
|
||||
) => true,
|
||||
}
|
||||
|
|
@ -574,7 +577,7 @@ impl<'tcx> Display for Const<'tcx> {
|
|||
/// Const-related utilities
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> {
|
||||
pub fn span_as_caller_location(self, span: Span) -> ConstValue {
|
||||
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
||||
let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
|
||||
self.const_caller_location(
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ impl<'tcx> ValTreeCreationError<'tcx> {
|
|||
|
||||
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
|
||||
pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>;
|
||||
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
|
||||
pub type EvalToConstValueResult<'tcx> = Result<ConstValue, ErrorHandled>;
|
||||
pub type EvalToValTreeResult<'tcx> = Result<ValTree<'tcx>, ValTreeCreationError<'tcx>>;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
|
|
|
|||
|
|
@ -143,10 +143,8 @@ impl<'tcx> MonoItem<'tcx> {
|
|||
};
|
||||
|
||||
// Similarly, the executable entrypoint must be instantiated exactly once.
|
||||
if let Some((entry_def_id, _)) = tcx.entry_fn(()) {
|
||||
if instance.def_id() == entry_def_id {
|
||||
return InstantiationMode::GloballyShared { may_conflict: false };
|
||||
}
|
||||
if tcx.is_entrypoint(instance.def_id()) {
|
||||
return InstantiationMode::GloballyShared { may_conflict: false };
|
||||
}
|
||||
|
||||
// If the function is #[naked] or contains any other attribute that requires exactly-once
|
||||
|
|
|
|||
|
|
@ -1465,7 +1465,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
|
|||
self.push(&format!("+ user_ty: {user_ty:?}"));
|
||||
}
|
||||
|
||||
let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| {
|
||||
let fmt_val = |val: ConstValue, ty: Ty<'tcx>| {
|
||||
let tcx = self.tcx;
|
||||
rustc_data_structures::make_display(move |fmt| {
|
||||
pretty_print_const_value_tcx(tcx, val, ty, fmt)
|
||||
|
|
@ -1562,16 +1562,12 @@ pub fn write_allocations<'tcx>(
|
|||
alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())
|
||||
}
|
||||
|
||||
fn alloc_id_from_const_val(val: ConstValue<'_>) -> Option<AllocId> {
|
||||
fn alloc_id_from_const_val(val: ConstValue) -> Option<AllocId> {
|
||||
match val {
|
||||
ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),
|
||||
ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,
|
||||
ConstValue::ZeroSized => None,
|
||||
ConstValue::Slice { .. } => {
|
||||
// `u8`/`str` slices, shouldn't contain pointers that we want to print.
|
||||
None
|
||||
}
|
||||
ConstValue::Indirect { alloc_id, .. } => {
|
||||
ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => {
|
||||
// FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
|
||||
// Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
|
||||
Some(alloc_id)
|
||||
|
|
@ -1885,7 +1881,7 @@ fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Resul
|
|||
fn comma_sep<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
fmt: &mut Formatter<'_>,
|
||||
elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
|
||||
elems: Vec<(ConstValue, Ty<'tcx>)>,
|
||||
) -> fmt::Result {
|
||||
let mut first = true;
|
||||
for (ct, ty) in elems {
|
||||
|
|
@ -1900,7 +1896,7 @@ fn comma_sep<'tcx>(
|
|||
|
||||
fn pretty_print_const_value_tcx<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ct: ConstValue<'tcx>,
|
||||
ct: ConstValue,
|
||||
ty: Ty<'tcx>,
|
||||
fmt: &mut Formatter<'_>,
|
||||
) -> fmt::Result {
|
||||
|
|
@ -1947,7 +1943,7 @@ fn pretty_print_const_value_tcx<'tcx>(
|
|||
let ct = tcx.lift(ct).unwrap();
|
||||
let ty = tcx.lift(ty).unwrap();
|
||||
if let Some(contents) = tcx.try_destructure_mir_constant_for_user_output(ct, ty) {
|
||||
let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
|
||||
let fields: Vec<(ConstValue, Ty<'_>)> = contents.fields.to_vec();
|
||||
match *ty.kind() {
|
||||
ty::Array(..) => {
|
||||
fmt.write_str("[")?;
|
||||
|
|
@ -2028,7 +2024,7 @@ fn pretty_print_const_value_tcx<'tcx>(
|
|||
}
|
||||
|
||||
pub(crate) fn pretty_print_const_value<'tcx>(
|
||||
ct: ConstValue<'tcx>,
|
||||
ct: ConstValue,
|
||||
ty: Ty<'tcx>,
|
||||
fmt: &mut Formatter<'_>,
|
||||
) -> fmt::Result {
|
||||
|
|
|
|||
|
|
@ -173,5 +173,5 @@ pub enum AnnotationSource {
|
|||
#[derive(Copy, Clone, Debug, HashStable)]
|
||||
pub struct DestructuredConstant<'tcx> {
|
||||
pub variant: Option<VariantIdx>,
|
||||
pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)],
|
||||
pub fields: &'tcx [(ConstValue, Ty<'tcx>)],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,8 +153,8 @@ impl EraseType for Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
|
|||
type Result = [u8; size_of::<Result<mir::ConstAlloc<'static>, mir::interpret::ErrorHandled>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Result<mir::ConstValue<'_>, mir::interpret::ErrorHandled> {
|
||||
type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()];
|
||||
impl EraseType for Result<mir::ConstValue, mir::interpret::ErrorHandled> {
|
||||
type Result = [u8; size_of::<Result<mir::ConstValue, mir::interpret::ErrorHandled>>()];
|
||||
}
|
||||
|
||||
impl EraseType for EvalToValTreeResult<'_> {
|
||||
|
|
@ -301,6 +301,7 @@ trivial! {
|
|||
rustc_middle::middle::resolve_bound_vars::ResolvedArg,
|
||||
rustc_middle::middle::stability::DeprecationEntry,
|
||||
rustc_middle::mir::ConstQualifs,
|
||||
rustc_middle::mir::ConstValue,
|
||||
rustc_middle::mir::interpret::AllocId,
|
||||
rustc_middle::mir::interpret::CtfeProvenance,
|
||||
rustc_middle::mir::interpret::ErrorHandled,
|
||||
|
|
@ -362,7 +363,6 @@ tcx_lifetime! {
|
|||
rustc_middle::mir::Const,
|
||||
rustc_middle::mir::DestructuredConstant,
|
||||
rustc_middle::mir::ConstAlloc,
|
||||
rustc_middle::mir::ConstValue,
|
||||
rustc_middle::mir::interpret::GlobalId,
|
||||
rustc_middle::mir::interpret::LitToConstInput,
|
||||
rustc_middle::mir::interpret::EvalStaticInitializerRawResult,
|
||||
|
|
|
|||
|
|
@ -1363,7 +1363,7 @@ rustc_queries! {
|
|||
}
|
||||
|
||||
/// Converts a type-level constant value into a MIR constant value.
|
||||
query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> {
|
||||
query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue {
|
||||
desc { "converting type-level constant value to MIR constant value"}
|
||||
}
|
||||
|
||||
|
|
@ -2152,9 +2152,6 @@ rustc_queries! {
|
|||
desc { |tcx| "collecting child items of module `{}`", tcx.def_path_str(def_id) }
|
||||
separate_provide_extern
|
||||
}
|
||||
query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option<CrateNum> {
|
||||
desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
||||
/// Gets the number of definitions in a foreign crate.
|
||||
///
|
||||
|
|
@ -2285,9 +2282,6 @@ rustc_queries! {
|
|||
query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>> {
|
||||
desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
query maybe_unused_trait_imports(_: ()) -> &'tcx FxIndexSet<LocalDefId> {
|
||||
desc { "fetching potentially unused trait imports" }
|
||||
}
|
||||
|
||||
/// All available crates in the graph, including those that should not be user-facing
|
||||
/// (such as private crates).
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
pub mod tls;
|
||||
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
use std::borrow::Borrow;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::cmp::Ordering;
|
||||
use std::env::VarError;
|
||||
use std::ffi::OsStr;
|
||||
|
|
@ -1625,7 +1625,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
/// Allocates a read-only byte or string literal for `mir::interpret` with alignment 1.
|
||||
/// Returns the same `AllocId` if called again with the same bytes.
|
||||
pub fn allocate_bytes_dedup(self, bytes: &[u8], salt: usize) -> interpret::AllocId {
|
||||
pub fn allocate_bytes_dedup<'a>(
|
||||
self,
|
||||
bytes: impl Into<Cow<'a, [u8]>>,
|
||||
salt: usize,
|
||||
) -> interpret::AllocId {
|
||||
// Create an allocation that just contains these bytes.
|
||||
let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes, ());
|
||||
let alloc = self.mk_const_alloc(alloc);
|
||||
|
|
@ -3373,6 +3377,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.resolutions(()).module_children.get(&def_id).map_or(&[], |v| &v[..])
|
||||
}
|
||||
|
||||
/// Return the crate imported by given use item.
|
||||
pub fn extern_mod_stmt_cnum(self, def_id: LocalDefId) -> Option<CrateNum> {
|
||||
self.resolutions(()).extern_crate_map.get(&def_id).copied()
|
||||
}
|
||||
|
||||
pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Arc<ast::Crate>)> {
|
||||
self.resolver_for_lowering_raw(()).0
|
||||
}
|
||||
|
|
@ -3410,6 +3419,20 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
pub fn do_not_recommend_impl(self, def_id: DefId) -> bool {
|
||||
self.get_diagnostic_attr(def_id, sym::do_not_recommend).is_some()
|
||||
}
|
||||
|
||||
/// Whether this def is one of the special bin crate entrypoint functions that must have a
|
||||
/// monomorphization and also not be internalized in the bin crate.
|
||||
pub fn is_entrypoint(self, def_id: DefId) -> bool {
|
||||
if self.is_lang_item(def_id, LangItem::Start) {
|
||||
return true;
|
||||
}
|
||||
if let Some((entry_def_id, _)) = self.entry_fn(())
|
||||
&& entry_def_id == def_id
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameter attributes that can only be determined by examining the body of a function instead
|
||||
|
|
@ -3428,10 +3451,6 @@ pub struct DeducedParamAttrs {
|
|||
}
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
providers.maybe_unused_trait_imports =
|
||||
|tcx, ()| &tcx.resolutions(()).maybe_unused_trait_imports;
|
||||
providers.extern_mod_stmt_cnum =
|
||||
|tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned();
|
||||
providers.is_panic_runtime =
|
||||
|tcx, LocalCrate| contains_name(tcx.hir_krate_attrs(), sym::panic_runtime);
|
||||
providers.is_compiler_builtins =
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
//! to help with the tedium.
|
||||
|
||||
use std::fmt::{self, Debug};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use rustc_abi::TyAndLayout;
|
||||
use rustc_hir::def::Namespace;
|
||||
|
|
@ -234,6 +235,7 @@ TrivialLiftImpls! {
|
|||
rustc_abi::ExternAbi,
|
||||
rustc_abi::Size,
|
||||
rustc_hir::Safety,
|
||||
rustc_middle::mir::ConstValue,
|
||||
rustc_type_ir::BoundConstness,
|
||||
rustc_type_ir::PredicatePolarity,
|
||||
// tidy-alphabetical-end
|
||||
|
|
@ -250,7 +252,7 @@ TrivialTypeTraversalImpls! {
|
|||
crate::mir::BlockTailInfo,
|
||||
crate::mir::BorrowKind,
|
||||
crate::mir::CastKind,
|
||||
crate::mir::ConstValue<'tcx>,
|
||||
crate::mir::ConstValue,
|
||||
crate::mir::CoroutineSavedLocal,
|
||||
crate::mir::FakeReadCause,
|
||||
crate::mir::Local,
|
||||
|
|
@ -311,6 +313,13 @@ TrivialTypeTraversalAndLiftImpls! {
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// Lift implementations
|
||||
|
||||
impl<'tcx> Lift<TyCtxt<'tcx>> for PhantomData<&()> {
|
||||
type Lifted = PhantomData<&'tcx ()>;
|
||||
fn lift_to_interner(self, _: TyCtxt<'tcx>) -> Option<Self::Lifted> {
|
||||
Some(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: Lift<TyCtxt<'tcx>>> Lift<TyCtxt<'tcx>> for Option<T> {
|
||||
type Lifted = Option<T::Lifted>;
|
||||
fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use rustc_abi::Size;
|
||||
use rustc_ast as ast;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::mir::interpret::{Allocation, CTFE_ALLOC_SALT, LitToConstInput, Scalar};
|
||||
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, LitToConstInput, Scalar};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::{
|
||||
|
|
@ -120,17 +120,18 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
|
|||
|
||||
let value = match (lit, lit_ty.kind()) {
|
||||
(ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
|
||||
let s = s.as_str();
|
||||
let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes(), ());
|
||||
let allocation = tcx.mk_const_alloc(allocation);
|
||||
ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
|
||||
let s = s.as_str().as_bytes();
|
||||
let len = s.len();
|
||||
let allocation = tcx.allocate_bytes_dedup(s, CTFE_ALLOC_SALT);
|
||||
ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() }
|
||||
}
|
||||
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _))
|
||||
(ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _))
|
||||
if matches!(inner_ty.kind(), ty::Slice(_)) =>
|
||||
{
|
||||
let allocation = Allocation::from_bytes_byte_aligned_immutable(data.as_byte_str(), ());
|
||||
let allocation = tcx.mk_const_alloc(allocation);
|
||||
ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
|
||||
let data = byte_sym.as_byte_str();
|
||||
let len = data.len();
|
||||
let allocation = tcx.allocate_bytes_dedup(data, CTFE_ALLOC_SALT);
|
||||
ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() }
|
||||
}
|
||||
(ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
|
||||
let id = tcx.allocate_bytes_dedup(byte_sym.as_byte_str(), CTFE_ALLOC_SALT);
|
||||
|
|
@ -138,10 +139,10 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
|
|||
}
|
||||
(ast::LitKind::CStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) =>
|
||||
{
|
||||
let allocation =
|
||||
Allocation::from_bytes_byte_aligned_immutable(byte_sym.as_byte_str(), ());
|
||||
let allocation = tcx.mk_const_alloc(allocation);
|
||||
ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
|
||||
let data = byte_sym.as_byte_str();
|
||||
let len = data.len();
|
||||
let allocation = tcx.allocate_bytes_dedup(data, CTFE_ALLOC_SALT);
|
||||
ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() }
|
||||
}
|
||||
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
|
||||
ConstValue::Scalar(Scalar::from_uint(n, Size::from_bytes(1)))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! See docs in `build/expr/mod.rs`.
|
||||
|
||||
use rustc_abi::{BackendRepr, FieldIdx, Primitive};
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::bug;
|
||||
|
|
@ -9,7 +9,6 @@ use rustc_middle::mir::interpret::Scalar;
|
|||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::cast::{CastTy, mir_cast_kind};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, Ty, UpvarArgs};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
|
@ -200,8 +199,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
{
|
||||
let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
|
||||
let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not));
|
||||
let layout =
|
||||
this.tcx.layout_of(this.typing_env().as_query_input(source_expr.ty));
|
||||
let discr = this.temp(discr_ty, source_expr.span);
|
||||
this.cfg.push_assign(
|
||||
block,
|
||||
|
|
@ -209,80 +206,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
discr,
|
||||
Rvalue::Discriminant(temp.into()),
|
||||
);
|
||||
let (op, ty) = (Operand::Move(discr), discr_ty);
|
||||
|
||||
if let BackendRepr::Scalar(scalar) = layout.unwrap().backend_repr
|
||||
&& !scalar.is_always_valid(&this.tcx)
|
||||
&& let Primitive::Int(int_width, _signed) = scalar.primitive()
|
||||
{
|
||||
let unsigned_ty = int_width.to_ty(this.tcx, false);
|
||||
let unsigned_place = this.temp(unsigned_ty, expr_span);
|
||||
this.cfg.push_assign(
|
||||
block,
|
||||
source_info,
|
||||
unsigned_place,
|
||||
Rvalue::Cast(CastKind::IntToInt, Operand::Copy(discr), unsigned_ty),
|
||||
);
|
||||
|
||||
let bool_ty = this.tcx.types.bool;
|
||||
let range = scalar.valid_range(&this.tcx);
|
||||
let merge_op =
|
||||
if range.start <= range.end { BinOp::BitAnd } else { BinOp::BitOr };
|
||||
|
||||
let mut comparer = |range: u128, bin_op: BinOp| -> Place<'tcx> {
|
||||
// We can use `ty::TypingEnv::fully_monomorphized()` here
|
||||
// as we only need it to compute the layout of a primitive.
|
||||
let range_val = Const::from_bits(
|
||||
this.tcx,
|
||||
range,
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
unsigned_ty,
|
||||
);
|
||||
let lit_op = this.literal_operand(expr.span, range_val);
|
||||
let is_bin_op = this.temp(bool_ty, expr_span);
|
||||
this.cfg.push_assign(
|
||||
block,
|
||||
source_info,
|
||||
is_bin_op,
|
||||
Rvalue::BinaryOp(
|
||||
bin_op,
|
||||
Box::new((Operand::Copy(unsigned_place), lit_op)),
|
||||
),
|
||||
);
|
||||
is_bin_op
|
||||
};
|
||||
let assert_place = if range.start == 0 {
|
||||
comparer(range.end, BinOp::Le)
|
||||
} else {
|
||||
let start_place = comparer(range.start, BinOp::Ge);
|
||||
let end_place = comparer(range.end, BinOp::Le);
|
||||
let merge_place = this.temp(bool_ty, expr_span);
|
||||
this.cfg.push_assign(
|
||||
block,
|
||||
source_info,
|
||||
merge_place,
|
||||
Rvalue::BinaryOp(
|
||||
merge_op,
|
||||
Box::new((
|
||||
Operand::Move(start_place),
|
||||
Operand::Move(end_place),
|
||||
)),
|
||||
),
|
||||
);
|
||||
merge_place
|
||||
};
|
||||
this.cfg.push(
|
||||
block,
|
||||
Statement::new(
|
||||
source_info,
|
||||
StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume(
|
||||
Operand::Move(assert_place),
|
||||
))),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
(op, ty)
|
||||
(Operand::Move(discr), discr_ty)
|
||||
} else {
|
||||
let ty = source_expr.ty;
|
||||
let source = unpack!(
|
||||
|
|
|
|||
|
|
@ -1045,11 +1045,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_float_into_constval<'tcx>(
|
||||
num: Symbol,
|
||||
float_ty: ty::FloatTy,
|
||||
neg: bool,
|
||||
) -> Option<ConstValue<'tcx>> {
|
||||
fn parse_float_into_constval(num: Symbol, float_ty: ty::FloatTy, neg: bool) -> Option<ConstValue> {
|
||||
parse_float_into_scalar(num, float_ty, neg).map(|s| ConstValue::Scalar(s.into()))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
|
||||
|
|
@ -83,8 +84,18 @@ pub(super) fn extract_refined_covspans<'tcx>(
|
|||
// Discard any span that overlaps with a hole.
|
||||
discard_spans_overlapping_holes(&mut covspans, &holes);
|
||||
|
||||
// Perform more refinement steps after holes have been dealt with.
|
||||
// Discard spans that overlap in unwanted ways.
|
||||
let mut covspans = remove_unwanted_overlapping_spans(covspans);
|
||||
|
||||
// For all empty spans, either enlarge them to be non-empty, or discard them.
|
||||
let source_map = tcx.sess.source_map();
|
||||
covspans.retain_mut(|covspan| {
|
||||
let Some(span) = ensure_non_empty_span(source_map, covspan.span) else { return false };
|
||||
covspan.span = span;
|
||||
true
|
||||
});
|
||||
|
||||
// Merge covspans that can be merged.
|
||||
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
|
||||
|
||||
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
|
||||
|
|
@ -230,3 +241,26 @@ fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
|
|||
// - Both have the same start and span A extends further right
|
||||
.then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse())
|
||||
}
|
||||
|
||||
fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
|
||||
if !span.is_empty() {
|
||||
return Some(span);
|
||||
}
|
||||
|
||||
// The span is empty, so try to enlarge it to cover an adjacent '{' or '}'.
|
||||
source_map
|
||||
.span_to_source(span, |src, start, end| try {
|
||||
// Adjusting span endpoints by `BytePos(1)` is normally a bug,
|
||||
// but in this case we have specifically checked that the character
|
||||
// we're skipping over is one of two specific ASCII characters, so
|
||||
// adjusting by exactly 1 byte is correct.
|
||||
if src.as_bytes().get(end).copied() == Some(b'{') {
|
||||
Some(span.with_hi(span.hi() + BytePos(1)))
|
||||
} else if start > 0 && src.as_bytes()[start - 1] == b'}' {
|
||||
Some(span.with_lo(span.lo() - BytePos(1)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok()?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1542,7 +1542,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
fn op_to_prop_const<'tcx>(
|
||||
ecx: &mut InterpCx<'tcx, DummyMachine>,
|
||||
op: &OpTy<'tcx>,
|
||||
) -> Option<ConstValue<'tcx>> {
|
||||
) -> Option<ConstValue> {
|
||||
// Do not attempt to propagate unsized locals.
|
||||
if op.layout.is_unsized() {
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
//! it's usually never invoked in this way.
|
||||
|
||||
use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind};
|
||||
use rustc_middle::ty::{TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{TyCtxt, TypeFlags, TypeVisitableExt};
|
||||
use rustc_trait_selection::traits;
|
||||
use tracing::trace;
|
||||
|
||||
|
|
@ -36,14 +36,23 @@ use crate::pass_manager::MirPass;
|
|||
pub(crate) struct ImpossiblePredicates;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for ImpossiblePredicates {
|
||||
#[tracing::instrument(level = "trace", skip(self, tcx, body))]
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let predicates = tcx
|
||||
.predicates_of(body.source.def_id())
|
||||
.predicates
|
||||
.iter()
|
||||
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
|
||||
if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
|
||||
trace!("found unsatisfiable predicates for {:?}", body.source);
|
||||
tracing::trace!(def_id = ?body.source.def_id());
|
||||
let predicates = tcx.predicates_of(body.source.def_id()).instantiate_identity(tcx);
|
||||
tracing::trace!(?predicates);
|
||||
let predicates = predicates.predicates.into_iter().filter(|p| {
|
||||
!p.has_type_flags(
|
||||
// Only consider global clauses to simplify.
|
||||
TypeFlags::HAS_FREE_LOCAL_NAMES
|
||||
// Clauses that refer to unevaluated constants as they cause cycles.
|
||||
| TypeFlags::HAS_CT_PROJECTION,
|
||||
)
|
||||
});
|
||||
let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect();
|
||||
tracing::trace!(?predicates);
|
||||
if predicates.references_error() || traits::impossible_predicates(tcx, predicates) {
|
||||
trace!("found unsatisfiable predicates");
|
||||
// Clear the body to only contain a single `unreachable` statement.
|
||||
let bbs = body.basic_blocks.as_mut();
|
||||
bbs.raw.truncate(1);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ fn remove_successors_from_switch<'tcx>(
|
|||
let is_unreachable = |bb| unreachable_blocks.contains(&bb);
|
||||
|
||||
// If there are multiple targets, we want to keep information about reachability for codegen.
|
||||
// For example (see tests/codegen/match-optimizes-away.rs)
|
||||
// For example (see tests/codegen-llvm/match-optimizes-away.rs)
|
||||
//
|
||||
// pub enum Two { A, B }
|
||||
// pub fn identity(x: Two) -> Two {
|
||||
|
|
|
|||
|
|
@ -659,10 +659,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Evaluates a *not yet monomorphized* constant.
|
||||
fn eval_constant(
|
||||
&mut self,
|
||||
constant: &mir::ConstOperand<'tcx>,
|
||||
) -> Option<mir::ConstValue<'tcx>> {
|
||||
fn eval_constant(&mut self, constant: &mir::ConstOperand<'tcx>) -> Option<mir::ConstValue> {
|
||||
let const_ = self.monomorphize(constant.const_);
|
||||
// Evaluate the constant. This makes const eval failure a collection-time error (rather than
|
||||
// a codegen-time error). rustc stops after collection if there was an error, so this
|
||||
|
|
@ -1355,19 +1352,15 @@ fn visit_mentioned_item<'tcx>(
|
|||
#[instrument(skip(tcx, output), level = "debug")]
|
||||
fn collect_const_value<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
value: mir::ConstValue<'tcx>,
|
||||
value: mir::ConstValue,
|
||||
output: &mut MonoItems<'tcx>,
|
||||
) {
|
||||
match value {
|
||||
mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => {
|
||||
collect_alloc(tcx, ptr.provenance.alloc_id(), output)
|
||||
}
|
||||
mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
|
||||
mir::ConstValue::Slice { data, meta: _ } => {
|
||||
for &prov in data.inner().provenance().ptrs().values() {
|
||||
collect_alloc(tcx, prov.alloc_id(), output);
|
||||
}
|
||||
}
|
||||
mir::ConstValue::Indirect { alloc_id, .. }
|
||||
| mir::ConstValue::Slice { alloc_id, meta: _ } => collect_alloc(tcx, alloc_id, output),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1582,6 +1575,15 @@ impl<'v> RootCollector<'_, 'v> {
|
|||
return;
|
||||
};
|
||||
|
||||
let main_instance = Instance::mono(self.tcx, main_def_id);
|
||||
if self.tcx.should_codegen_locally(main_instance) {
|
||||
self.output.push(create_fn_mono_item(
|
||||
self.tcx,
|
||||
main_instance,
|
||||
self.tcx.def_span(main_def_id),
|
||||
));
|
||||
}
|
||||
|
||||
let Some(start_def_id) = self.tcx.lang_items().start_fn() else {
|
||||
self.tcx.dcx().emit_fatal(errors::StartNotFound);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -223,11 +223,7 @@ where
|
|||
// So even if its mode is LocalCopy, we need to treat it like a root.
|
||||
match mono_item.instantiation_mode(cx.tcx) {
|
||||
InstantiationMode::GloballyShared { .. } => {}
|
||||
InstantiationMode::LocalCopy => {
|
||||
if !cx.tcx.is_lang_item(mono_item.def_id(), LangItem::Start) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
InstantiationMode::LocalCopy => continue,
|
||||
}
|
||||
|
||||
let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
|
||||
|
|
@ -821,10 +817,9 @@ fn mono_item_visibility<'tcx>(
|
|||
| InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden,
|
||||
};
|
||||
|
||||
// The `start_fn` lang item is actually a monomorphized instance of a
|
||||
// function in the standard library, used for the `main` function. We don't
|
||||
// want to export it so we tag it with `Hidden` visibility but this symbol
|
||||
// is only referenced from the actual `main` symbol which we unfortunately
|
||||
// Both the `start_fn` lang item and `main` itself should not be exported,
|
||||
// so we give them with `Hidden` visibility but these symbols are
|
||||
// only referenced from the actual `main` symbol which we unfortunately
|
||||
// don't know anything about during partitioning/collection. As a result we
|
||||
// forcibly keep this symbol out of the `internalization_candidates` set.
|
||||
//
|
||||
|
|
@ -834,7 +829,7 @@ fn mono_item_visibility<'tcx>(
|
|||
// from the `main` symbol we'll generate later.
|
||||
//
|
||||
// This may be fixable with a new `InstanceKind` perhaps? Unsure!
|
||||
if tcx.is_lang_item(def_id, LangItem::Start) {
|
||||
if tcx.is_entrypoint(def_id) {
|
||||
*can_be_internalized = false;
|
||||
return Visibility::Hidden;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,23 +126,29 @@ pub(super) fn report_suspicious_mismatch_block(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_unclosed_delims_error(
|
||||
unmatched: UnmatchedDelim,
|
||||
psess: &ParseSess,
|
||||
) -> Option<Diag<'_>> {
|
||||
// `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
|
||||
// `unmatched_delims` only for error recovery in the `Parser`.
|
||||
let found_delim = unmatched.found_delim?;
|
||||
let mut spans = vec![unmatched.found_span];
|
||||
if let Some(sp) = unmatched.unclosed_span {
|
||||
spans.push(sp);
|
||||
};
|
||||
let err = psess.dcx().create_err(MismatchedClosingDelimiter {
|
||||
spans,
|
||||
delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()).to_string(),
|
||||
unmatched: unmatched.found_span,
|
||||
opening_candidate: unmatched.candidate_span,
|
||||
unclosed: unmatched.unclosed_span,
|
||||
});
|
||||
Some(err)
|
||||
pub(crate) fn make_errors_for_mismatched_closing_delims<'psess>(
|
||||
unmatcheds: &[UnmatchedDelim],
|
||||
psess: &'psess ParseSess,
|
||||
) -> Vec<Diag<'psess>> {
|
||||
unmatcheds
|
||||
.iter()
|
||||
.filter_map(|unmatched| {
|
||||
// `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
|
||||
// `unmatched_delims` only for error recovery in the `Parser`.
|
||||
let found_delim = unmatched.found_delim?;
|
||||
let mut spans = vec![unmatched.found_span];
|
||||
if let Some(sp) = unmatched.unclosed_span {
|
||||
spans.push(sp);
|
||||
};
|
||||
let err = psess.dcx().create_err(MismatchedClosingDelimiter {
|
||||
spans,
|
||||
delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind())
|
||||
.to_string(),
|
||||
unmatched: unmatched.found_span,
|
||||
opening_candidate: unmatched.candidate_span,
|
||||
unclosed: unmatched.unclosed_span,
|
||||
});
|
||||
Some(err)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use diagnostics::make_unclosed_delims_error;
|
||||
use diagnostics::make_errors_for_mismatched_closing_delims;
|
||||
use rustc_ast::ast::{self, AttrStyle};
|
||||
use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
|
|
@ -71,27 +71,23 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
|
|||
};
|
||||
let res = lexer.lex_token_trees(/* is_delimited */ false);
|
||||
|
||||
let mut unmatched_delims: Vec<_> = lexer
|
||||
.diag_info
|
||||
.unmatched_delims
|
||||
.into_iter()
|
||||
.filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess))
|
||||
.collect();
|
||||
let mut unmatched_closing_delims: Vec<_> =
|
||||
make_errors_for_mismatched_closing_delims(&lexer.diag_info.unmatched_delims, psess);
|
||||
|
||||
match res {
|
||||
Ok((_open_spacing, stream)) => {
|
||||
if unmatched_delims.is_empty() {
|
||||
if unmatched_closing_delims.is_empty() {
|
||||
Ok(stream)
|
||||
} else {
|
||||
// Return error if there are unmatched delimiters or unclosed delimiters.
|
||||
Err(unmatched_delims)
|
||||
Err(unmatched_closing_delims)
|
||||
}
|
||||
}
|
||||
Err(errs) => {
|
||||
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
|
||||
// because the delimiter mismatch is more likely to be the root cause of error
|
||||
unmatched_delims.extend(errs);
|
||||
Err(unmatched_delims)
|
||||
unmatched_closing_delims.extend(errs);
|
||||
Err(unmatched_closing_delims)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,45 +51,6 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
|
|||
}
|
||||
}
|
||||
|
||||
fn eof_err(&mut self) -> Diag<'psess> {
|
||||
let msg = "this file contains an unclosed delimiter";
|
||||
let mut err = self.dcx().struct_span_err(self.token.span, msg);
|
||||
|
||||
let unclosed_delimiter_show_limit = 5;
|
||||
let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_delimiters.len());
|
||||
for &(_, span) in &self.diag_info.open_delimiters[..len] {
|
||||
err.span_label(span, "unclosed delimiter");
|
||||
self.diag_info.unmatched_delims.push(UnmatchedDelim {
|
||||
found_delim: None,
|
||||
found_span: self.token.span,
|
||||
unclosed_span: Some(span),
|
||||
candidate_span: None,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some((_, span)) = self.diag_info.open_delimiters.get(unclosed_delimiter_show_limit)
|
||||
&& self.diag_info.open_delimiters.len() >= unclosed_delimiter_show_limit + 2
|
||||
{
|
||||
err.span_label(
|
||||
*span,
|
||||
format!(
|
||||
"another {} unclosed delimiters begin from here",
|
||||
self.diag_info.open_delimiters.len() - unclosed_delimiter_show_limit
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((delim, _)) = self.diag_info.open_delimiters.last() {
|
||||
report_suspicious_mismatch_block(
|
||||
&mut err,
|
||||
&self.diag_info,
|
||||
self.psess.source_map(),
|
||||
*delim,
|
||||
)
|
||||
}
|
||||
err
|
||||
}
|
||||
|
||||
fn lex_token_tree_open_delim(
|
||||
&mut self,
|
||||
open_delim: Delimiter,
|
||||
|
|
@ -206,13 +167,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
|
|||
} else if let Some(glued) = self.token.glue(&next_tok) {
|
||||
self.token = glued;
|
||||
} else {
|
||||
let this_spacing = if next_tok.is_punct() {
|
||||
Spacing::Joint
|
||||
} else if next_tok == token::Eof {
|
||||
Spacing::Alone
|
||||
} else {
|
||||
Spacing::JointHidden
|
||||
};
|
||||
let this_spacing = self.calculate_spacing(&next_tok);
|
||||
break (this_spacing, next_tok);
|
||||
}
|
||||
};
|
||||
|
|
@ -223,23 +178,64 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
|
|||
// Cut-down version of `bump` used when the token kind is known in advance.
|
||||
fn bump_minimal(&mut self) -> Spacing {
|
||||
let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor();
|
||||
|
||||
let this_spacing = if is_next_tok_preceded_by_whitespace {
|
||||
Spacing::Alone
|
||||
} else {
|
||||
if next_tok.is_punct() {
|
||||
Spacing::Joint
|
||||
} else if next_tok == token::Eof {
|
||||
Spacing::Alone
|
||||
} else {
|
||||
Spacing::JointHidden
|
||||
}
|
||||
self.calculate_spacing(&next_tok)
|
||||
};
|
||||
|
||||
self.token = next_tok;
|
||||
this_spacing
|
||||
}
|
||||
|
||||
fn calculate_spacing(&self, next_tok: &Token) -> Spacing {
|
||||
if next_tok.is_punct() {
|
||||
Spacing::Joint
|
||||
} else if *next_tok == token::Eof {
|
||||
Spacing::Alone
|
||||
} else {
|
||||
Spacing::JointHidden
|
||||
}
|
||||
}
|
||||
|
||||
fn eof_err(&mut self) -> Diag<'psess> {
|
||||
const UNCLOSED_DELIMITER_SHOW_LIMIT: usize = 5;
|
||||
let msg = "this file contains an unclosed delimiter";
|
||||
let mut err = self.dcx().struct_span_err(self.token.span, msg);
|
||||
|
||||
let len = usize::min(UNCLOSED_DELIMITER_SHOW_LIMIT, self.diag_info.open_delimiters.len());
|
||||
for &(_, span) in &self.diag_info.open_delimiters[..len] {
|
||||
err.span_label(span, "unclosed delimiter");
|
||||
self.diag_info.unmatched_delims.push(UnmatchedDelim {
|
||||
found_delim: None,
|
||||
found_span: self.token.span,
|
||||
unclosed_span: Some(span),
|
||||
candidate_span: None,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some((_, span)) = self.diag_info.open_delimiters.get(UNCLOSED_DELIMITER_SHOW_LIMIT)
|
||||
&& self.diag_info.open_delimiters.len() >= UNCLOSED_DELIMITER_SHOW_LIMIT + 2
|
||||
{
|
||||
err.span_label(
|
||||
*span,
|
||||
format!(
|
||||
"another {} unclosed delimiters begin from here",
|
||||
self.diag_info.open_delimiters.len() - UNCLOSED_DELIMITER_SHOW_LIMIT
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((delim, _)) = self.diag_info.open_delimiters.last() {
|
||||
report_suspicious_mismatch_block(
|
||||
&mut err,
|
||||
&self.diag_info,
|
||||
self.psess.source_map(),
|
||||
*delim,
|
||||
)
|
||||
}
|
||||
err
|
||||
}
|
||||
|
||||
fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> {
|
||||
// An unexpected closing delimiter (i.e., there is no matching opening delimiter).
|
||||
let token_str = token_to_string(&self.token);
|
||||
|
|
|
|||
|
|
@ -285,6 +285,9 @@ pub fn check_builtin_meta_item(
|
|||
| sym::rustc_do_not_implement_via_object
|
||||
| sym::rustc_coinductive
|
||||
| sym::const_trait
|
||||
| sym::stable
|
||||
| sym::unstable
|
||||
| sym::rustc_allowed_through_unstable_modules
|
||||
| sym::rustc_specialization_trait
|
||||
| sym::rustc_unsafe_specialization_marker
|
||||
| sym::rustc_allow_incoherent_impl
|
||||
|
|
@ -303,6 +306,8 @@ pub fn check_builtin_meta_item(
|
|||
| sym::cold
|
||||
| sym::target_feature
|
||||
| sym::rustc_allow_const_fn_unstable
|
||||
| sym::macro_use
|
||||
| sym::macro_escape
|
||||
| sym::naked
|
||||
| sym::no_mangle
|
||||
| sym::non_exhaustive
|
||||
|
|
|
|||
|
|
@ -227,6 +227,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => {
|
||||
self.check_link_section(hir_id, *attr_span, span, target)
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => {
|
||||
self.check_macro_use(hir_id, sym::macro_use, *span, target)
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::MacroEscape(span)) => {
|
||||
self.check_macro_use(hir_id, sym::macro_escape, *span, target)
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
|
||||
self.check_naked(hir_id, *attr_span, span, target)
|
||||
}
|
||||
|
|
@ -362,9 +368,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
|
||||
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
|
||||
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
|
||||
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
|
||||
self.check_macro_use(hir_id, attr, target)
|
||||
}
|
||||
[sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod),
|
||||
[sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
|
||||
[sym::should_panic, ..] => {
|
||||
|
|
@ -1255,7 +1258,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() {
|
||||
if self.tcx.extern_mod_stmt_cnum(hir_id.owner.def_id).is_none() {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
|
|
@ -2411,17 +2414,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
|
||||
let Some(name) = attr.name() else {
|
||||
return;
|
||||
};
|
||||
fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) {
|
||||
match target {
|
||||
Target::ExternCrate | Target::Mod => {}
|
||||
_ => {
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span(),
|
||||
attr_span,
|
||||
errors::MacroUse { name },
|
||||
);
|
||||
}
|
||||
|
|
@ -2474,7 +2474,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
// Warn on useless empty attributes.
|
||||
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
|
||||
let note = if attr.has_any_name(&[
|
||||
sym::macro_use,
|
||||
sym::allow,
|
||||
sym::expect,
|
||||
sym::warn,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use rustc_errors::{MultiSpan, listify};
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::intravisit::{self, InferKind, Visitor};
|
||||
use rustc_hir::{AmbigArg, ForeignItemKind, ItemId, ItemKind, PatKind};
|
||||
use rustc_hir::{AmbigArg, ForeignItemId, ItemId, PatKind};
|
||||
use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
|
|
@ -599,18 +599,13 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
|||
|
||||
DefKind::Struct | DefKind::Union => {
|
||||
// While structs and unions have type privacy, their fields do not.
|
||||
let item = self.tcx.hir_expect_item(def_id);
|
||||
if let hir::ItemKind::Struct(_, _, ref struct_def)
|
||||
| hir::ItemKind::Union(_, _, ref struct_def) = item.kind
|
||||
{
|
||||
for field in struct_def.fields() {
|
||||
let field_vis = self.tcx.local_visibility(field.def_id);
|
||||
if field_vis.is_accessible_from(module, self.tcx) {
|
||||
self.reach(field.def_id, macro_ev).ty();
|
||||
}
|
||||
let struct_def = self.tcx.adt_def(def_id);
|
||||
for field in struct_def.non_enum_variant().fields.iter() {
|
||||
let def_id = field.did.expect_local();
|
||||
let field_vis = self.tcx.local_visibility(def_id);
|
||||
if field_vis.is_accessible_from(module, self.tcx) {
|
||||
self.reach(def_id, macro_ev).ty();
|
||||
}
|
||||
} else {
|
||||
bug!("item {:?} with DefKind {:?}", item, def_kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1644,66 +1639,29 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
|
|||
self.check(def_id, item_visibility, effective_vis).generics().predicates();
|
||||
}
|
||||
DefKind::Enum => {
|
||||
let item = tcx.hir_item(id);
|
||||
if let hir::ItemKind::Enum(_, _, ref def) = item.kind {
|
||||
self.check_unnameable(item.owner_id.def_id, effective_vis);
|
||||
self.check_unnameable(def_id, effective_vis);
|
||||
self.check(def_id, item_visibility, effective_vis).generics().predicates();
|
||||
|
||||
self.check(item.owner_id.def_id, item_visibility, effective_vis)
|
||||
.generics()
|
||||
.predicates();
|
||||
|
||||
for variant in def.variants {
|
||||
for field in variant.data.fields() {
|
||||
self.check(field.def_id, item_visibility, effective_vis).ty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Subitems of foreign modules have their own publicity.
|
||||
DefKind::ForeignMod => {
|
||||
let item = tcx.hir_item(id);
|
||||
if let hir::ItemKind::ForeignMod { items, .. } = item.kind {
|
||||
for &foreign_item in items {
|
||||
let foreign_item = tcx.hir_foreign_item(foreign_item);
|
||||
|
||||
let ev = self.get(foreign_item.owner_id.def_id);
|
||||
let vis = tcx.local_visibility(foreign_item.owner_id.def_id);
|
||||
|
||||
if let ForeignItemKind::Type = foreign_item.kind {
|
||||
self.check_unnameable(foreign_item.owner_id.def_id, ev);
|
||||
}
|
||||
|
||||
self.check(foreign_item.owner_id.def_id, vis, ev)
|
||||
.generics()
|
||||
.predicates()
|
||||
.ty();
|
||||
}
|
||||
let adt = tcx.adt_def(id.owner_id);
|
||||
for field in adt.all_fields() {
|
||||
self.check(field.did.expect_local(), item_visibility, effective_vis).ty();
|
||||
}
|
||||
}
|
||||
// Subitems of structs and unions have their own publicity.
|
||||
DefKind::Struct | DefKind::Union => {
|
||||
let item = tcx.hir_item(id);
|
||||
if let hir::ItemKind::Struct(_, _, ref struct_def)
|
||||
| hir::ItemKind::Union(_, _, ref struct_def) = item.kind
|
||||
{
|
||||
self.check_unnameable(item.owner_id.def_id, effective_vis);
|
||||
self.check(item.owner_id.def_id, item_visibility, effective_vis)
|
||||
.generics()
|
||||
.predicates();
|
||||
self.check_unnameable(def_id, effective_vis);
|
||||
self.check(def_id, item_visibility, effective_vis).generics().predicates();
|
||||
|
||||
for field in struct_def.fields() {
|
||||
let field_visibility = tcx.local_visibility(field.def_id);
|
||||
let field_ev = self.get(field.def_id);
|
||||
let adt = tcx.adt_def(id.owner_id);
|
||||
for field in adt.all_fields() {
|
||||
let visibility = min(item_visibility, field.vis.expect_local(), tcx);
|
||||
let field_ev = self.get(field.did.expect_local());
|
||||
|
||||
self.check(
|
||||
field.def_id,
|
||||
min(item_visibility, field_visibility, tcx),
|
||||
field_ev,
|
||||
)
|
||||
.ty();
|
||||
}
|
||||
self.check(field.did.expect_local(), visibility, field_ev).ty();
|
||||
}
|
||||
}
|
||||
// Subitems of foreign modules have their own publicity.
|
||||
DefKind::ForeignMod => {}
|
||||
// An inherent impl is public when its type is public
|
||||
// Subitems of inherent impls have their own publicity.
|
||||
// A trait impl is public when both its type and its trait are public
|
||||
|
|
@ -1763,6 +1721,19 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_foreign_item(&mut self, id: ForeignItemId) {
|
||||
let tcx = self.tcx;
|
||||
let def_id = id.owner_id.def_id;
|
||||
let item_visibility = tcx.local_visibility(def_id);
|
||||
let effective_vis = self.get(def_id);
|
||||
|
||||
if let DefKind::ForeignTy = self.tcx.def_kind(def_id) {
|
||||
self.check_unnameable(def_id, effective_vis);
|
||||
}
|
||||
|
||||
self.check(def_id, item_visibility, effective_vis).generics().predicates().ty();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
|
|
@ -1791,20 +1762,13 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
|
|||
if let Some(body_id) = tcx.hir_maybe_body_owned_by(def_id) {
|
||||
visitor.visit_nested_body(body_id.id());
|
||||
}
|
||||
}
|
||||
|
||||
for id in module.free_items() {
|
||||
if let ItemKind::Impl(i) = tcx.hir_item(id).kind {
|
||||
if let Some(item) = i.of_trait {
|
||||
let trait_ref = tcx.impl_trait_ref(id.owner_id.def_id).unwrap();
|
||||
let trait_ref = trait_ref.instantiate_identity();
|
||||
visitor.span = item.path.span;
|
||||
let _ = visitor.visit_def_id(
|
||||
trait_ref.def_id,
|
||||
"trait",
|
||||
&trait_ref.print_only_trait_path(),
|
||||
);
|
||||
}
|
||||
if let DefKind::Impl { of_trait: true } = tcx.def_kind(def_id) {
|
||||
let trait_ref = tcx.impl_trait_ref(def_id).unwrap();
|
||||
let trait_ref = trait_ref.instantiate_identity();
|
||||
visitor.span = tcx.hir_expect_item(def_id).expect_impl().of_trait.unwrap().path.span;
|
||||
let _ =
|
||||
visitor.visit_def_id(trait_ref.def_id, "trait", &trait_ref.print_only_trait_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1895,7 +1859,11 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) {
|
|||
// Check for private types in public interfaces.
|
||||
let mut checker = PrivateItemsInPublicInterfacesChecker { tcx, effective_visibilities };
|
||||
|
||||
for id in tcx.hir_free_items() {
|
||||
let crate_items = tcx.hir_crate_items(());
|
||||
for id in crate_items.free_items() {
|
||||
checker.check_item(id);
|
||||
}
|
||||
for id in crate_items.foreign_items() {
|
||||
checker.check_foreign_item(id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue