Merge from rustc

This commit is contained in:
The Miri Conjob Bot 2023-10-28 05:19:39 +00:00
commit d7e49c03f6
572 changed files with 9703 additions and 4609 deletions

View File

@ -4557,9 +4557,7 @@ dependencies = [
"rustc-demangle",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_macros",
"rustc_middle",
"rustc_session",
"rustc_span",
@ -4694,6 +4692,7 @@ dependencies = [
"arrayvec",
"askama",
"expect-test",
"indexmap 2.0.0",
"itertools",
"minifier",
"once_cell",

View File

@ -18,7 +18,6 @@
#![feature(new_uninit)]
#![feature(maybe_uninit_slice)]
#![feature(decl_macro)]
#![feature(pointer_byte_offsets)]
#![feature(rustc_attrs)]
#![cfg_attr(test, feature(test))]
#![feature(strict_provenance)]

View File

@ -734,6 +734,8 @@ pub enum RangeSyntax {
}
/// All the different flavors of pattern that Rust recognizes.
//
// Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum PatKind {
/// Represents a wildcard pattern (`_`).
@ -967,6 +969,7 @@ impl Stmt {
}
}
// Adding a new variant? Please update `test_stmt` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum StmtKind {
/// A local (let) binding.
@ -1345,6 +1348,7 @@ pub struct StructExpr {
pub rest: StructRest,
}
// Adding a new variant? Please update `test_expr` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum ExprKind {
/// An array (`[a, b, c, d]`)
@ -2015,6 +2019,8 @@ pub struct BareFnTy {
}
/// The various kinds of type recognized by the compiler.
//
// Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum TyKind {
/// A variable-length slice (`[T]`).
@ -2880,6 +2886,7 @@ pub struct ConstItem {
pub expr: Option<P<Expr>>,
}
// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum ItemKind {
/// An `extern crate` item, with the optional *original* crate name if the crate was renamed.

View File

@ -188,7 +188,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
e.id,
None,
e.span,
hir::AsyncCoroutineKind::Block,
hir::CoroutineSource::Block,
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
),
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
@ -598,7 +598,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
closure_node_id: NodeId,
ret_ty: Option<hir::FnRetTy<'hir>>,
span: Span,
async_gen_kind: hir::AsyncCoroutineKind,
async_gen_kind: hir::CoroutineSource,
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
) -> hir::ExprKind<'hir> {
let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span)));
@ -1005,7 +1005,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
inner_closure_id,
async_ret_ty,
body.span,
hir::AsyncCoroutineKind::Closure,
hir::CoroutineSource::Closure,
|this| this.with_new_scopes(|this| this.lower_expr_mut(body)),
);
let hir_id = this.lower_node_id(inner_closure_id);

View File

@ -1206,7 +1206,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
closure_id,
None,
body.span,
hir::AsyncCoroutineKind::Fn,
hir::CoroutineSource::Fn,
|this| {
// Create a block from the user's function body:
let user_body = this.lower_block_expr(body);

View File

@ -1217,7 +1217,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir_id: this.lower_node_id(node_id),
body: this.lower_const_body(path_expr.span, Some(&path_expr)),
});
return GenericArg::Const(ConstArg { value: ct, span });
return GenericArg::Const(ConstArg {
value: ct,
span,
is_desugared_from_effects: false,
});
}
}
}
@ -1228,6 +1232,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ast::GenericArg::Const(ct) => GenericArg::Const(ConstArg {
value: self.lower_anon_const(&ct),
span: self.lower_span(ct.value.span),
is_desugared_from_effects: false,
}),
}
}
@ -2525,6 +2530,7 @@ impl<'hir> GenericArgsCtor<'hir> {
self.args.push(hir::GenericArg::Const(hir::ConstArg {
value: hir::AnonConst { def_id, hir_id, body },
span,
is_desugared_from_effects: true,
}))
}

View File

@ -13,6 +13,7 @@ use rustc_session::parse::{feature_err, ParseSess};
use rustc_session::Session;
use rustc_span::hygiene::Transparency;
use rustc_span::{symbol::sym, symbol::Symbol, Span};
use std::fmt::{self, Display};
use std::num::NonZeroU32;
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
@ -23,10 +24,7 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591).
pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
pub fn rust_version_symbol() -> Symbol {
let version = option_env!("CFG_RELEASE").unwrap_or("<current>");
Symbol::intern(&version)
}
pub const CURRENT_RUSTC_VERSION: &str = env!("CFG_RELEASE");
pub fn is_builtin_attr(attr: &Attribute) -> bool {
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
@ -144,13 +142,24 @@ pub enum StabilityLevel {
/// `#[stable]`
Stable {
/// Rust release which stabilized this feature.
since: Symbol,
since: Since,
/// Is this item allowed to be referred to on stable, despite being contained in unstable
/// modules?
allowed_through_unstable_modules: bool,
},
}
/// Rust release in which a feature is stabilized.
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
pub enum Since {
Version(Version),
/// Stabilized in the upcoming version, whatever number that is.
Current,
/// Failed to parse a stabilization version.
Err,
}
impl StabilityLevel {
pub fn is_unstable(&self) -> bool {
matches!(self, StabilityLevel::Unstable { .. })
@ -372,22 +381,24 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit
let since = if let Some(since) = since {
if since.as_str() == VERSION_PLACEHOLDER {
Ok(rust_version_symbol())
} else if parse_version(since.as_str(), false).is_some() {
Ok(since)
Since::Current
} else if let Some(version) = parse_version(since.as_str(), false) {
Since::Version(version)
} else {
Err(sess.emit_err(session_diagnostics::InvalidSince { span: attr.span }))
sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
Since::Err
}
} else {
Err(sess.emit_err(session_diagnostics::MissingSince { span: attr.span }))
sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
Since::Err
};
match (feature, since) {
(Ok(feature), Ok(since)) => {
match feature {
Ok(feature) => {
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
Some((feature, level))
}
(Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
Err(ErrorGuaranteed { .. }) => None,
}
}
@ -556,11 +567,12 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Version {
major: u16,
minor: u16,
patch: u16,
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(HashStable_Generic)]
pub struct Version {
pub major: u16,
pub minor: u16,
pub patch: u16,
}
fn parse_version(s: &str, allow_appendix: bool) -> Option<Version> {
@ -576,6 +588,12 @@ fn parse_version(s: &str, allow_appendix: bool) -> Option<Version> {
Some(Version { major, minor, patch })
}
impl Display for Version {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
/// evaluate individual items.
pub fn eval_condition(
@ -609,7 +627,7 @@ pub fn eval_condition(
sess.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span });
return false;
};
let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap();
let rustc_version = parse_version(CURRENT_RUSTC_VERSION, true).unwrap();
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
if sess.assume_incomplete_release {

View File

@ -8,7 +8,7 @@ use rustc_errors::{
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
use rustc_hir::{AsyncCoroutineKind, CoroutineKind, LangItem};
use rustc_hir::{CoroutineKind, CoroutineSource, LangItem};
use rustc_infer::traits::ObligationCause;
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::mir::tcx::PlaceTy;
@ -2506,8 +2506,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let kind = match use_span.coroutine_kind() {
Some(coroutine_kind) => match coroutine_kind {
CoroutineKind::Async(async_kind) => match async_kind {
AsyncCoroutineKind::Block => "async block",
AsyncCoroutineKind::Closure => "async closure",
CoroutineSource::Block => "async block",
CoroutineSource::Closure => "async closure",
_ => bug!("async block/closure expected, but async function found."),
},
CoroutineKind::Coroutine => "coroutine",

View File

@ -682,9 +682,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
};
let mir_description = match hir.body(body).coroutine_kind {
Some(hir::CoroutineKind::Async(gen)) => match gen {
hir::AsyncCoroutineKind::Block => " of async block",
hir::AsyncCoroutineKind::Closure => " of async closure",
hir::AsyncCoroutineKind::Fn => {
hir::CoroutineSource::Block => " of async block",
hir::CoroutineSource::Closure => " of async closure",
hir::CoroutineSource::Fn => {
let parent_item =
hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
let output = &parent_item

View File

@ -8,7 +8,7 @@ use rustc_infer::infer::InferCtxt;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::ty::{self, RegionVid, Ty};
use rustc_span::{Span, DUMMY_SP};
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
use std::rc::Rc;
use type_op::TypeOpOutput;
@ -318,7 +318,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
.param_env
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
.fully_perform(self.infcx, DUMMY_SP)
.unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty));
.map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty))
.ok()?;
debug!(?bounds, ?constraints);
self.add_outlives_bounds(bounds);
constraints

View File

@ -37,8 +37,9 @@
//! following snippet
//!
//! ```rust
//! # #![allow(dead_code)]
//! struct A { x : i32 }
//! struct A {
//! x: i32,
//! }
//!
//! struct B(i32);
//!
@ -74,6 +75,7 @@
//! trait PartialEq {
//! fn eq(&self, other: &Self) -> bool;
//! }
//!
//! impl PartialEq for i32 {
//! fn eq(&self, other: &i32) -> bool {
//! *self == *other
@ -90,22 +92,22 @@
//!
//! ```text
//! Struct(vec![FieldInfo {
//! span: <span of x>
//! name: Some(<ident of x>),
//! self_: <expr for &self.x>,
//! other: vec![<expr for &other.x]
//! }])
//! span: <span of x>,
//! name: Some(<ident of x>),
//! self_: <expr for &self.x>,
//! other: vec![<expr for &other.x>],
//! }])
//! ```
//!
//! For the `B` impl, called with `B(a)` and `B(b)`,
//!
//! ```text
//! Struct(vec![FieldInfo {
//! span: <span of `i32`>,
//! name: None,
//! self_: <expr for &a>
//! other: vec![<expr for &b>]
//! }])
//! span: <span of i32>,
//! name: None,
//! self_: <expr for &a>,
//! other: vec![<expr for &b>],
//! }])
//! ```
//!
//! ## Enums
@ -114,33 +116,42 @@
//! == C0(b)`, the SubstructureFields is
//!
//! ```text
//! EnumMatching(0, <ast::Variant for C0>,
//! vec![FieldInfo {
//! span: <span of i32>
//! name: None,
//! self_: <expr for &a>,
//! other: vec![<expr for &b>]
//! }])
//! EnumMatching(
//! 0,
//! <ast::Variant for C0>,
//! vec![FieldInfo {
//! span: <span of i32>,
//! name: None,
//! self_: <expr for &a>,
//! other: vec![<expr for &b>],
//! }],
//! )
//! ```
//!
//! For `C1 {x}` and `C1 {x}`,
//!
//! ```text
//! EnumMatching(1, <ast::Variant for C1>,
//! vec![FieldInfo {
//! span: <span of x>
//! name: Some(<ident of x>),
//! self_: <expr for &self.x>,
//! other: vec![<expr for &other.x>]
//! }])
//! EnumMatching(
//! 1,
//! <ast::Variant for C1>,
//! vec![FieldInfo {
//! span: <span of x>,
//! name: Some(<ident of x>),
//! self_: <expr for &self.x>,
//! other: vec![<expr for &other.x>],
//! }],
//! )
//! ```
//!
//! For the tags,
//!
//! ```text
//! EnumTag(
//! &[<ident of self tag>, <ident of other tag>], <expr to combine with>)
//! &[<ident of self tag>, <ident of other tag>],
//! <expr to combine with>,
//! )
//! ```
//!
//! Note that this setup doesn't allow for the brute-force "match every variant
//! against every other variant" approach, which is bad because it produces a
//! quadratic amount of code (see #15375).
@ -154,9 +165,13 @@
//!
//! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>]))
//!
//! StaticEnum(<ast::EnumDef of C>,
//! vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])),
//! (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))])
//! StaticEnum(
//! <ast::EnumDef of C>,
//! vec![
//! (<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])),
//! (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)])),
//! ],
//! )
//! ```
pub use StaticFields::*;
@ -522,7 +537,10 @@ impl<'a> TraitDef<'a> {
/// Given that we are deriving a trait `DerivedTrait` for a type like:
///
/// ```ignore (only-for-syntax-highlight)
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z>
/// where
/// C: WhereTrait,
/// {
/// a: A,
/// b: B::Item,
/// b1: <B as DeclaredTrait>::Item,
@ -535,12 +553,13 @@ impl<'a> TraitDef<'a> {
/// create an impl like:
///
/// ```ignore (only-for-syntax-highlight)
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
/// C: WhereTrait,
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z>
/// where
/// C: WhereTrait,
/// A: DerivedTrait + B1 + ... + BN,
/// B: DerivedTrait + B1 + ... + BN,
/// C: DerivedTrait + B1 + ... + BN,
/// B::Item: DerivedTrait + B1 + ... + BN,
/// B::Item: DerivedTrait + B1 + ... + BN,
/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
/// ...
/// {
@ -676,65 +695,59 @@ impl<'a> TraitDef<'a> {
}
}));
{
// Extra scope required here so ty_params goes out of scope before params is moved
let ty_param_names: Vec<Symbol> = params
.iter()
.filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. }))
.map(|ty_param| ty_param.ident.name)
.collect();
let mut ty_params = params
.iter()
.filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. }))
.peekable();
if !ty_param_names.is_empty() {
for field_ty in field_tys {
let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx);
if ty_params.peek().is_some() {
let ty_param_names: Vec<Symbol> =
ty_params.map(|ty_param| ty_param.ident.name).collect();
for field_ty in field_tys {
let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx);
for field_ty_param in field_ty_params {
// if we have already handled this type, skip it
if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind
&& let [sole_segment] = &*p.segments
&& ty_param_names.contains(&sole_segment.ident.name)
{
continue;
}
let mut bounds: Vec<_> = self
.additional_bounds
.iter()
.map(|p| {
cx.trait_bound(
p.to_path(cx, self.span, type_ident, generics),
self.is_const,
)
})
.collect();
// Require the current trait.
if !self.skip_path_as_bound {
bounds.push(cx.trait_bound(trait_path.clone(), self.is_const));
}
// Add a `Copy` bound if required.
if is_packed && self.needs_copy_as_bound_if_packed {
let p = deriving::path_std!(marker::Copy);
bounds.push(cx.trait_bound(
for field_ty_param in field_ty_params {
// if we have already handled this type, skip it
if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind
&& let [sole_segment] = &*p.segments
&& ty_param_names.contains(&sole_segment.ident.name)
{
continue;
}
let mut bounds: Vec<_> = self
.additional_bounds
.iter()
.map(|p| {
cx.trait_bound(
p.to_path(cx, self.span, type_ident, generics),
self.is_const,
));
}
)
})
.collect();
if !bounds.is_empty() {
let predicate = ast::WhereBoundPredicate {
span: self.span,
bound_generic_params: field_ty_param.bound_generic_params,
bounded_ty: field_ty_param.ty,
bounds,
};
// Require the current trait.
if !self.skip_path_as_bound {
bounds.push(cx.trait_bound(trait_path.clone(), self.is_const));
}
let predicate = ast::WherePredicate::BoundPredicate(predicate);
where_clause.predicates.push(predicate);
}
// Add a `Copy` bound if required.
if is_packed && self.needs_copy_as_bound_if_packed {
let p = deriving::path_std!(marker::Copy);
bounds.push(cx.trait_bound(
p.to_path(cx, self.span, type_ident, generics),
self.is_const,
));
}
if !bounds.is_empty() {
let predicate = ast::WhereBoundPredicate {
span: self.span,
bound_generic_params: field_ty_param.bound_generic_params,
bounded_ty: field_ty_param.ty,
bounds,
};
let predicate = ast::WherePredicate::BoundPredicate(predicate);
where_clause.predicates.push(predicate);
}
}
}
@ -1026,6 +1039,7 @@ impl<'a> MethodDef<'a> {
}
/// The normal case uses field access.
///
/// ```
/// #[derive(PartialEq)]
/// # struct Dummy;
@ -1038,10 +1052,12 @@ impl<'a> MethodDef<'a> {
/// }
/// }
/// ```
///
/// But if the struct is `repr(packed)`, we can't use something like
/// `&self.x` because that might cause an unaligned ref. So for any trait
/// method that takes a reference, we use a local block to force a copy.
/// This requires that the field impl `Copy`.
///
/// ```rust,ignore (example)
/// # struct A { x: u8, y: u8 }
/// impl PartialEq for A {
@ -1053,7 +1069,7 @@ impl<'a> MethodDef<'a> {
/// impl Hash for A {
/// fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
/// ::core::hash::Hash::hash(&{ self.x }, state);
/// ::core::hash::Hash::hash(&{ self.y }, state)
/// ::core::hash::Hash::hash(&{ self.y }, state);
/// }
/// }
/// ```
@ -1107,7 +1123,9 @@ impl<'a> MethodDef<'a> {
/// A2(i32)
/// }
/// ```
///
/// is equivalent to:
///
/// ```
/// #![feature(core_intrinsics)]
/// enum A {
@ -1119,15 +1137,15 @@ impl<'a> MethodDef<'a> {
/// fn eq(&self, other: &A) -> bool {
/// let __self_tag = ::core::intrinsics::discriminant_value(self);
/// let __arg1_tag = ::core::intrinsics::discriminant_value(other);
/// __self_tag == __arg1_tag &&
/// match (self, other) {
/// (A::A2(__self_0), A::A2(__arg1_0)) =>
/// *__self_0 == *__arg1_0,
/// __self_tag == __arg1_tag
/// && match (self, other) {
/// (A::A2(__self_0), A::A2(__arg1_0)) => *__self_0 == *__arg1_0,
/// _ => true,
/// }
/// }
/// }
/// ```
///
/// Creates a tag check combined with a match for a tuple of all
/// `selflike_args`, with an arm for each variant with fields, possibly an
/// arm for each fieldless variant (if `unify_fieldless_variants` is not
@ -1349,7 +1367,7 @@ impl<'a> MethodDef<'a> {
// (Variant1, Variant1, ...) => Body1
// (Variant2, Variant2, ...) => Body2,
// ...
// _ => ::core::intrinsics::unreachable()
// _ => ::core::intrinsics::unreachable(),
// }
let get_match_expr = |mut selflike_args: ThinVec<P<Expr>>| {
let match_arg = if selflike_args.len() == 1 {

View File

@ -362,9 +362,14 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
// currently use this mode so we have to allow it -- but we absolutely
// shouldn't let any more targets do that.
// (Also see <https://github.com/rust-lang/rust/issues/115666>.)
//
// The unstable abi `PtxKernel` also uses Direct for now.
// It needs to switch to something else before stabilization can happen.
// (See issue: https://github.com/rust-lang/rust/issues/117271)
assert!(
matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64"),
"`PassMode::Direct` for aggregates only allowed on wasm targets\nProblematic type: {:#?}",
matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64")
|| self.conv == Conv::PtxKernel,
"`PassMode::Direct` for aggregates only allowed on wasm and `extern \"ptx-kernel\"` fns\nProblematic type: {:#?}",
arg.layout,
);
}

View File

@ -22,7 +22,9 @@ use rustc_session::utils::NativeLibKind;
/// need out of the shared crate context before we get rid of it.
use rustc_session::{filesearch, Session};
use rustc_span::symbol::Symbol;
use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
use rustc_target::spec::crt_objects::CrtObjects;
use rustc_target::spec::LinkSelfContainedComponents;
use rustc_target::spec::LinkSelfContainedDefault;
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy};
use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
@ -728,6 +730,7 @@ fn link_natively<'a>(
) -> Result<(), ErrorGuaranteed> {
info!("preparing {:?} to {:?}", crate_type, out_filename);
let (linker_path, flavor) = linker_and_flavor(sess);
let self_contained_components = self_contained_components(sess, crate_type);
let mut cmd = linker_with_args(
&linker_path,
flavor,
@ -737,6 +740,7 @@ fn link_natively<'a>(
tmpdir,
out_filename,
codegen_results,
self_contained_components,
)?;
linker::disable_localization(&mut cmd);
@ -812,14 +816,14 @@ fn link_natively<'a>(
"Linker does not support -static-pie command line option. Retrying with -static instead."
);
// Mirror `add_(pre,post)_link_objects` to replace CRT objects.
let self_contained = self_contained(sess, crate_type);
let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled();
let opts = &sess.target;
let pre_objects = if self_contained {
let pre_objects = if self_contained_crt_objects {
&opts.pre_link_objects_self_contained
} else {
&opts.pre_link_objects
};
let post_objects = if self_contained {
let post_objects = if self_contained_crt_objects {
&opts.post_link_objects_self_contained
} else {
&opts.post_link_objects
@ -830,7 +834,9 @@ fn link_natively<'a>(
.iter()
.copied()
.flatten()
.map(|obj| get_object_file_path(sess, obj, self_contained).into_os_string())
.map(|obj| {
get_object_file_path(sess, obj, self_contained_crt_objects).into_os_string()
})
.collect::<Vec<_>>()
};
let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe);
@ -1710,26 +1716,43 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
/// Various toolchain components used during linking are used from rustc distribution
/// instead of being found somewhere on the host system.
/// We only provide such support for a very limited number of targets.
fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
if sess.target.link_self_contained == LinkSelfContainedDefault::False {
sess.emit_err(errors::UnsupportedLinkSelfContained);
}
return self_contained;
}
fn self_contained_components(sess: &Session, crate_type: CrateType) -> LinkSelfContainedComponents {
// Turn the backwards compatible bool values for `self_contained` into fully inferred
// `LinkSelfContainedComponents`.
let self_contained =
if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
// Emit an error if the user requested self-contained mode on the CLI but the target
// explicitly refuses it.
if sess.target.link_self_contained.is_disabled() {
sess.emit_err(errors::UnsupportedLinkSelfContained);
}
self_contained
} else {
match sess.target.link_self_contained {
LinkSelfContainedDefault::False => false,
LinkSelfContainedDefault::True => true,
match sess.target.link_self_contained {
LinkSelfContainedDefault::False => false,
LinkSelfContainedDefault::True => true,
// FIXME: Find a better heuristic for "native musl toolchain is available",
// based on host and linker path, for example.
// (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237).
LinkSelfContainedDefault::Musl => sess.crt_static(Some(crate_type)),
LinkSelfContainedDefault::Mingw => {
sess.host == sess.target
&& sess.target.vendor != "uwp"
&& detect_self_contained_mingw(&sess)
}
LinkSelfContainedDefault::WithComponents(components) => {
// For target specs with explicitly enabled components, we can return them
// directly.
return components;
}
// FIXME: Find a better heuristic for "native musl toolchain is available",
// based on host and linker path, for example.
// (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237).
LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)),
LinkSelfContainedDefault::InferredForMingw => {
sess.host == sess.target
&& sess.target.vendor != "uwp"
&& detect_self_contained_mingw(&sess)
}
}
};
if self_contained {
LinkSelfContainedComponents::all()
} else {
LinkSelfContainedComponents::empty()
}
}
@ -1889,37 +1912,14 @@ fn add_linked_symbol_object(
return;
};
// NOTE(nbdd0121): MSVC will hang if the input object file contains no sections,
// so add an empty section.
if file.format() == object::BinaryFormat::Coff {
// NOTE(nbdd0121): MSVC will hang if the input object file contains no sections,
// so add an empty section.
file.add_section(Vec::new(), ".text".into(), object::SectionKind::Text);
// We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the
// default mangler in `object` crate.
file.set_mangling(object::write::Mangling::None);
// Add feature flags to the object file. On MSVC this is optional but LLD will complain if
// not present.
let mut feature = 0;
if file.architecture() == object::Architecture::I386 {
// Indicate that all SEH handlers are registered in .sxdata section.
// We don't have generate any code, so we don't need .sxdata section but LLD still
// expects us to set this bit (see #96498).
// Reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
feature |= 1;
}
file.add_symbol(object::write::Symbol {
name: "@feat.00".into(),
value: feature,
size: 0,
kind: object::SymbolKind::Data,
scope: object::SymbolScope::Compilation,
weak: false,
section: object::write::SymbolSection::Absolute,
flags: object::SymbolFlags::None,
});
}
for (sym, kind) in symbols.iter() {
@ -2053,13 +2053,14 @@ fn linker_with_args<'a>(
tmpdir: &Path,
out_filename: &Path,
codegen_results: &CodegenResults,
self_contained_components: LinkSelfContainedComponents,
) -> Result<Command, ErrorGuaranteed> {
let self_contained = self_contained(sess, crate_type);
let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled();
let cmd = &mut *super::linker::get_linker(
sess,
path,
flavor,
self_contained,
self_contained_components.are_any_components_enabled(),
&codegen_results.crate_info.target_cpu,
);
let link_output_kind = link_output_kind(sess, crate_type);
@ -2086,7 +2087,7 @@ fn linker_with_args<'a>(
// ------------ Object code and libraries, order-dependent ------------
// Pre-link CRT objects.
add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained);
add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects);
add_linked_symbol_object(
cmd,
@ -2229,7 +2230,7 @@ fn linker_with_args<'a>(
cmd,
sess,
link_output_kind,
self_contained,
self_contained_components,
flavor,
crate_type,
codegen_results,
@ -2245,7 +2246,7 @@ fn linker_with_args<'a>(
// ------------ Object code and libraries, order-dependent ------------
// Post-link CRT objects.
add_post_link_objects(cmd, sess, link_output_kind, self_contained);
add_post_link_objects(cmd, sess, link_output_kind, self_contained_crt_objects);
// ------------ Late order-dependent options ------------
@ -2262,7 +2263,7 @@ fn add_order_independent_options(
cmd: &mut dyn Linker,
sess: &Session,
link_output_kind: LinkOutputKind,
self_contained: bool,
self_contained_components: LinkSelfContainedComponents,
flavor: LinkerFlavor,
crate_type: CrateType,
codegen_results: &CodegenResults,
@ -2270,7 +2271,7 @@ fn add_order_independent_options(
tmpdir: &Path,
) {
// Take care of the flavors and CLI options requesting the `lld` linker.
add_lld_args(cmd, sess, flavor);
add_lld_args(cmd, sess, flavor, self_contained_components);
add_apple_sdk(cmd, sess, flavor);
@ -2295,7 +2296,7 @@ fn add_order_independent_options(
// Make the binary compatible with data execution prevention schemes.
cmd.add_no_exec();
if self_contained {
if self_contained_components.is_crt_objects_enabled() {
cmd.no_crt_objects();
}
@ -2326,7 +2327,7 @@ fn add_order_independent_options(
cmd.linker_plugin_lto();
add_library_search_dirs(cmd, sess, self_contained);
add_library_search_dirs(cmd, sess, self_contained_components.are_any_components_enabled());
cmd.output_filename(out_filename);
@ -2976,8 +2977,16 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro
/// invoke it:
/// - when the self-contained linker flag is active: the build of `lld` distributed with rustc,
/// - or any `lld` available to `cc`.
fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
debug!("add_lld_args requested, flavor: '{flavor:?}'");
fn add_lld_args(
cmd: &mut dyn Linker,
sess: &Session,
flavor: LinkerFlavor,
self_contained_components: LinkSelfContainedComponents,
) {
debug!(
"add_lld_args requested, flavor: '{:?}', target self-contained components: {:?}",
flavor, self_contained_components,
);
// If the flavor doesn't use a C/C++ compiler to invoke the linker, or doesn't opt in to `lld`,
// we don't need to do anything.
@ -2986,9 +2995,32 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
}
// 1. Implement the "self-contained" part of this feature by adding rustc distribution
// directories to the tool's search path:
// - if the self-contained linker is enabled on the CLI.
if sess.opts.cg.link_self_contained.is_linker_enabled() {
// directories to the tool's search path, depending on a mix between what users can specify on
// the CLI, and what the target spec enables (as it can't disable components):
// - if the self-contained linker is enabled on the CLI or by the target spec,
// - and if the self-contained linker is not disabled on the CLI.
let self_contained_cli = sess.opts.cg.link_self_contained.is_linker_enabled();
let self_contained_target = self_contained_components.is_linker_enabled();
// FIXME: in the future, codegen backends may need to have more control over this process: they
// don't always support all the features the linker expects here, and vice versa. For example,
// at the time of writing this, lld expects a newer style of aarch64 TLS relocations that
// cranelift doesn't implement yet. That in turn can impact whether linking would succeed on
// such a target when using the `cg_clif` backend and lld.
//
// Until interactions between backends and linker features are expressible, we limit target
// specs to opt-in to lld only when we're on the llvm backend, where it's expected to work and
// tested on CI. As usual, the CLI still has precedence over this, so that users and developers
// can still override this default when needed (e.g. for tests).
let uses_llvm_backend =
matches!(sess.opts.unstable_opts.codegen_backend.as_deref(), None | Some("llvm"));
if !uses_llvm_backend && !self_contained_cli && sess.opts.cg.linker_flavor.is_none() {
// We bail if we're not using llvm and lld was not explicitly requested on the CLI.
return;
}
let self_contained_linker = self_contained_cli || self_contained_target;
if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() {
for path in sess.get_tools_search_paths(false) {
cmd.arg({
let mut arg = OsString::from("-B");

View File

@ -228,6 +228,35 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
if sess.target.is_like_osx {
file.set_macho_build_version(macho_object_build_version_for_target(&sess.target))
}
if binary_format == BinaryFormat::Coff {
// Disable the default mangler to avoid mangling the special "@feat.00" symbol name.
let original_mangling = file.mangling();
file.set_mangling(object::write::Mangling::None);
let mut feature = 0;
if file.architecture() == object::Architecture::I386 {
// When linking with /SAFESEH on x86, lld requires that all linker inputs be marked as
// safe exception handling compatible. Metadata files masquerade as regular COFF
// objects and are treated as linker inputs, despite containing no actual code. Thus,
// they still need to be marked as safe exception handling compatible. See #96498.
// Reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
feature |= 1;
}
file.add_symbol(object::write::Symbol {
name: "@feat.00".into(),
value: feature,
size: 0,
kind: object::SymbolKind::Data,
scope: object::SymbolScope::Compilation,
weak: false,
section: object::write::SymbolSection::Absolute,
flags: object::SymbolFlags::None,
});
file.set_mangling(original_mangling);
}
let e_flags = match architecture {
Architecture::Mips => {
let arch = match sess.target.options.cpu.as_ref() {

View File

@ -15,7 +15,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher};
use rustc_hir::def_id::DefId;
use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
use rustc_hir::{AsyncCoroutineKind, CoroutineKind, Mutability};
use rustc_hir::{CoroutineKind, CoroutineSource, Mutability};
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
@ -560,9 +560,9 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &
fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
match coroutine_kind {
Some(CoroutineKind::Async(AsyncCoroutineKind::Block)) => "async_block",
Some(CoroutineKind::Async(AsyncCoroutineKind::Closure)) => "async_closure",
Some(CoroutineKind::Async(AsyncCoroutineKind::Fn)) => "async_fn",
Some(CoroutineKind::Async(CoroutineSource::Block)) => "async_block",
Some(CoroutineKind::Async(CoroutineSource::Closure)) => "async_closure",
Some(CoroutineKind::Async(CoroutineSource::Fn)) => "async_fn",
Some(CoroutineKind::Coroutine) => "coroutine",
None => "closure",
}

View File

@ -360,7 +360,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
pub struct Coroutine(pub hir::CoroutineKind);
impl<'tcx> NonConstOp<'tcx> for Coroutine {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
if let hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Block) = self.0 {
if let hir::CoroutineKind::Async(hir::CoroutineSource::Block) = self.0 {
Status::Unstable(sym::const_async_blocks)
} else {
Status::Forbidden
@ -372,8 +372,8 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine {
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let msg = format!("{}s are not allowed in {}s", self.0.descr(), ccx.const_kind());
if let hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Block) = self.0 {
let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
if let hir::CoroutineKind::Async(hir::CoroutineSource::Block) = self.0 {
ccx.tcx.sess.create_feature_err(
errors::UnallowedOpInConstContext { span, msg },
sym::const_async_blocks,

View File

@ -1,5 +1,6 @@
driver_impl_ice = the compiler unexpectedly panicked. this is a bug.
driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly
driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
driver_impl_ice_flags = compiler flags: {$flags}

View File

@ -60,7 +60,7 @@ use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::str;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::OnceLock;
use std::sync::{Arc, OnceLock};
use std::time::{Instant, SystemTime};
use time::OffsetDateTime;
@ -136,7 +136,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
rustc_query_system::DEFAULT_LOCALE_RESOURCE,
rustc_resolve::DEFAULT_LOCALE_RESOURCE,
rustc_session::DEFAULT_LOCALE_RESOURCE,
rustc_symbol_mangling::DEFAULT_LOCALE_RESOURCE,
rustc_trait_selection::DEFAULT_LOCALE_RESOURCE,
rustc_ty_utils::DEFAULT_LOCALE_RESOURCE,
// tidy-alphabetical-end
@ -223,11 +222,18 @@ pub struct RunCompiler<'a, 'b> {
file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
make_codegen_backend:
Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
using_internal_features: Arc<std::sync::atomic::AtomicBool>,
}
impl<'a, 'b> RunCompiler<'a, 'b> {
pub fn new(at_args: &'a [String], callbacks: &'b mut (dyn Callbacks + Send)) -> Self {
Self { at_args, callbacks, file_loader: None, make_codegen_backend: None }
Self {
at_args,
callbacks,
file_loader: None,
make_codegen_backend: None,
using_internal_features: Arc::default(),
}
}
/// Set a custom codegen backend.
@ -259,9 +265,23 @@ impl<'a, 'b> RunCompiler<'a, 'b> {
self
}
/// Set the session-global flag that checks whether internal features have been used,
/// suppressing the message about submitting an issue in ICEs when enabled.
#[must_use]
pub fn set_using_internal_features(mut self, using_internal_features: Arc<AtomicBool>) -> Self {
self.using_internal_features = using_internal_features;
self
}
/// Parse args and run the compiler.
pub fn run(self) -> interface::Result<()> {
run_compiler(self.at_args, self.callbacks, self.file_loader, self.make_codegen_backend)
run_compiler(
self.at_args,
self.callbacks,
self.file_loader,
self.make_codegen_backend,
self.using_internal_features,
)
}
}
@ -272,6 +292,7 @@ fn run_compiler(
make_codegen_backend: Option<
Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
>,
using_internal_features: Arc<std::sync::atomic::AtomicBool>,
) -> interface::Result<()> {
let mut early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default());
@ -316,6 +337,7 @@ fn run_compiler(
override_queries: None,
make_codegen_backend,
registry: diagnostics_registry(),
using_internal_features,
expanded_args: args,
};
@ -1333,8 +1355,12 @@ fn ice_path() -> &'static Option<PathBuf> {
/// If you have no extra info to report, pass the empty closure `|_| ()` as the argument to
/// extra_info.
///
/// Returns a flag that can be set to disable the note for submitting a bug. This can be passed to
/// [`RunCompiler::set_using_internal_features`] to let macro expansion set it when encountering
/// internal features.
///
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) {
pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) -> Arc<AtomicBool> {
// If the user has not explicitly overridden "RUST_BACKTRACE", then produce
// full backtraces. When a compiler ICE happens, we want to gather
// as much information as possible to present in the issue opened
@ -1345,6 +1371,8 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
std::env::set_var("RUST_BACKTRACE", "full");
}
let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default());
let using_internal_features_hook = using_internal_features.clone();
panic::update_hook(Box::new(
move |default_hook: &(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static),
info: &PanicInfo<'_>| {
@ -1394,9 +1422,11 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
}
// Print the ICE message
report_ice(info, bug_report_url, extra_info);
report_ice(info, bug_report_url, extra_info, &using_internal_features_hook);
},
));
using_internal_features
}
/// Prints the ICE message, including query stack, but without backtrace.
@ -1405,7 +1435,12 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
///
/// When `install_ice_hook` is called, this function will be called as the panic
/// hook.
fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler)) {
fn report_ice(
info: &panic::PanicInfo<'_>,
bug_report_url: &str,
extra_info: fn(&Handler),
using_internal_features: &AtomicBool,
) {
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
@ -1422,7 +1457,11 @@ fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(
handler.emit_err(session_diagnostics::Ice);
}
handler.emit_note(session_diagnostics::IceBugReport { bug_report_url });
if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) {
handler.emit_note(session_diagnostics::IceBugReportInternalFeature);
} else {
handler.emit_note(session_diagnostics::IceBugReport { bug_report_url });
}
let version = util::version_str!().unwrap_or("unknown_version");
let triple = config::host_triple();
@ -1506,7 +1545,7 @@ pub fn main() -> ! {
init_rustc_env_logger(&handler);
signal_handler::install();
let mut callbacks = TimePassesCallbacks::default();
install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
let exit_code = catch_with_exit_code(|| {
let args = env::args_os()
.enumerate()
@ -1516,7 +1555,9 @@ pub fn main() -> ! {
})
})
.collect::<Vec<_>>();
RunCompiler::new(&args, &mut callbacks).run()
RunCompiler::new(&args, &mut callbacks)
.set_using_internal_features(using_internal_features)
.run()
});
if let Some(format) = callbacks.time_passes {

View File

@ -42,6 +42,10 @@ pub(crate) struct IceBugReport<'a> {
pub bug_report_url: &'a str,
}
#[derive(Diagnostic)]
#[diag(driver_impl_ice_bug_report_internal_feature)]
pub(crate) struct IceBugReportInternalFeature;
#[derive(Diagnostic)]
#[diag(driver_impl_ice_version)]
pub(crate) struct IceVersion<'a> {

View File

@ -508,6 +508,8 @@ pub enum StashKey {
TraitMissingMethod,
OpaqueHiddenTypeMismatch,
MaybeForgetReturn,
/// Query cycle detected, stashing in favor of a better error.
Cycle,
}
fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) {

View File

@ -35,7 +35,7 @@ pub struct StripUnconfigured<'a> {
pub lint_node_id: NodeId,
}
pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -> Features {
fn feature_list(attr: &Attribute) -> ThinVec<ast::NestedMetaItem> {
if attr.has_name(sym::feature)
&& let Some(list) = attr.meta_item_list()
@ -167,6 +167,15 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
// If the declared feature is unstable, record it.
if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) {
(f.set_enabled)(&mut features);
// When the ICE comes from core, alloc or std (approximation of the standard library), there's a chance
// that the person hitting the ICE may be using -Zbuild-std or similar with an untested target.
// The bug is probably in the standard library and not the compiler in that case, but that doesn't
// really matter - we want a bug report.
if features.internal(name)
&& ![sym::core, sym::alloc, sym::std].contains(&crate_name)
{
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
}
features.set_declared_lang_feature(name, mi.span(), None);
continue;
}

View File

@ -246,6 +246,8 @@ impl<'hir> PathSegment<'hir> {
pub struct ConstArg {
pub value: AnonConst,
pub span: Span,
/// Indicates whether this comes from a `~const` desugaring.
pub is_desugared_from_effects: bool,
}
#[derive(Clone, Copy, Debug, HashStable_Generic)]
@ -400,7 +402,14 @@ impl<'hir> GenericArgs<'hir> {
/// This function returns the number of type and const generic params.
/// It should only be used for diagnostics.
pub fn num_generic_params(&self) -> usize {
self.args.iter().filter(|arg| !matches!(arg, GenericArg::Lifetime(_))).count()
self.args
.iter()
.filter(|arg| match arg {
GenericArg::Lifetime(_)
| GenericArg::Const(ConstArg { is_desugared_from_effects: true, .. }) => false,
_ => true,
})
.count()
}
/// The span encompassing the text inside the surrounding brackets.
@ -1511,7 +1520,7 @@ impl<'hir> Body<'hir> {
#[derive(HashStable_Generic, Encodable, Decodable)]
pub enum CoroutineKind {
/// An explicit `async` block or the body of an async function.
Async(AsyncCoroutineKind),
Async(CoroutineSource),
/// A coroutine literal created via a `yield` inside a closure.
Coroutine,
@ -1520,56 +1529,45 @@ pub enum CoroutineKind {
impl fmt::Display for CoroutineKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CoroutineKind::Async(k) => fmt::Display::fmt(k, f),
CoroutineKind::Async(k) => {
if f.alternate() {
f.write_str("`async` ")?;
} else {
f.write_str("async ")?
}
k.fmt(f)
}
CoroutineKind::Coroutine => f.write_str("coroutine"),
}
}
}
impl CoroutineKind {
pub fn descr(&self) -> &'static str {
match self {
CoroutineKind::Async(ask) => ask.descr(),
CoroutineKind::Coroutine => "coroutine",
}
}
}
/// In the case of a coroutine created as part of an async construct,
/// which kind of async construct caused it to be created?
/// In the case of a coroutine created as part of an async/gen construct,
/// which kind of async/gen construct caused it to be created?
///
/// This helps error messages but is also used to drive coercions in
/// type-checking (see #60424).
#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
#[derive(HashStable_Generic, Encodable, Decodable)]
pub enum AsyncCoroutineKind {
/// An explicit `async` block written by the user.
pub enum CoroutineSource {
/// An explicit `async`/`gen` block written by the user.
Block,
/// An explicit `async` closure written by the user.
/// An explicit `async`/`gen` closure written by the user.
Closure,
/// The `async` block generated as the body of an async function.
/// The `async`/`gen` block generated as the body of an async/gen function.
Fn,
}
impl fmt::Display for AsyncCoroutineKind {
impl fmt::Display for CoroutineSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
AsyncCoroutineKind::Block => "async block",
AsyncCoroutineKind::Closure => "async closure body",
AsyncCoroutineKind::Fn => "async fn body",
})
}
}
impl AsyncCoroutineKind {
pub fn descr(&self) -> &'static str {
match self {
AsyncCoroutineKind::Block => "`async` block",
AsyncCoroutineKind::Closure => "`async` closure body",
AsyncCoroutineKind::Fn => "`async fn` body",
CoroutineSource::Block => "block",
CoroutineSource::Closure => "closure body",
CoroutineSource::Fn => "fn body",
}
.fmt(f)
}
}
@ -3781,6 +3779,7 @@ impl<'hir> Node<'hir> {
ItemKind::TyAlias(ty, _)
| ItemKind::Static(ty, _, _)
| ItemKind::Const(ty, _, _) => Some(ty),
ItemKind::Impl(impl_item) => Some(&impl_item.self_ty),
_ => None,
},
Node::TraitItem(it) => match it.kind {

View File

@ -210,6 +210,7 @@ language_item_table! {
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0);
Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
CoroutineState, sym::coroutine_state, gen_state, Target::Enum, GenericRequirement::None;
Coroutine, sym::coroutine, gen_trait, Target::Trait, GenericRequirement::Minimum(1);

View File

@ -429,6 +429,14 @@ pub(crate) fn check_generic_arg_count(
.filter(|param| matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. }))
.count();
let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count;
let synth_const_param_count = gen_params
.params
.iter()
.filter(|param| {
matches!(param.kind, ty::GenericParamDefKind::Const { is_host_effect: true, .. })
})
.count();
let named_const_param_count = param_counts.consts - synth_const_param_count;
let infer_lifetimes =
(gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params();
@ -573,11 +581,13 @@ pub(crate) fn check_generic_arg_count(
debug!(?expected_min);
debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params());
let provided = gen_args.num_generic_params();
check_types_and_consts(
expected_min,
param_counts.consts + named_type_param_count,
param_counts.consts + named_type_param_count + synth_type_param_count,
gen_args.num_generic_params(),
named_const_param_count + named_type_param_count,
named_const_param_count + named_type_param_count + synth_type_param_count,
provided,
param_counts.lifetimes + has_self as usize,
gen_args.num_lifetime_params(),
)

View File

@ -128,7 +128,11 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
let param_env = tcx.param_env(item_def_id);
for field in &def.non_enum_variant().fields {
let field_ty = tcx.normalize_erasing_regions(param_env, field.ty(tcx, args));
let Ok(field_ty) = tcx.try_normalize_erasing_regions(param_env, field.ty(tcx, args))
else {
tcx.sess.delay_span_bug(span, "could not normalize field type");
continue;
};
if !allowed_union_field(field_ty, tcx, param_env) {
let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {

View File

@ -181,14 +181,11 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
})?;
}
tcx.sess.track_errors(|| {
tcx.sess.time("impl_wf_inference", || {
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module))
});
})?;
tcx.sess.track_errors(|| {
tcx.sess.time("coherence_checking", || {
// Check impls constrain their parameters
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module));
for &trait_def_id in tcx.all_local_trait_impls(()).keys() {
tcx.ensure().coherent_trait(trait_def_id);
}
@ -205,15 +202,19 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
})?;
}
tcx.sess.time("wf_checking", || {
let errs = tcx.sess.time("wf_checking", || {
tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
})?;
});
// NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync.
tcx.sess.time("item_types_checking", || {
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
});
// HACK: `check_mod_type_wf` may spuriously emit errors due to `delay_span_bug`, even if those errors
// only actually get emitted in `check_mod_item_types`.
errs?;
if tcx.features().rustc_attrs {
tcx.sess.track_errors(|| collect::test_opaque_hidden_types(tcx))?;
}

View File

@ -129,6 +129,44 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
if self.missing_lifetimes() { "lifetime" } else { "generic" }
}
/// Returns true if the generic type is a trait
/// and is being referred to from one of its trait impls
fn is_in_trait_impl(&self) -> bool {
if self.tcx.is_trait(self.def_id) {
// Here we check if the reference to the generic type
// is from the 'of_trait' field of the enclosing impl
let parent = self.tcx.hir().get_parent(self.path_segment.hir_id);
let parent_item = self
.tcx
.hir()
.get_by_def_id(self.tcx.hir().get_parent_item(self.path_segment.hir_id).def_id);
// Get the HIR id of the trait ref
let hir::Node::TraitRef(hir::TraitRef { hir_ref_id: trait_ref_id, .. }) = parent else {
return false;
};
// Get the HIR id of the 'of_trait' field of the impl
let hir::Node::Item(hir::Item {
kind:
hir::ItemKind::Impl(hir::Impl {
of_trait: Some(hir::TraitRef { hir_ref_id: id_in_of_trait, .. }),
..
}),
..
}) = parent_item
else {
return false;
};
// Check that trait is referred to from the of_trait field of impl
trait_ref_id == id_in_of_trait
} else {
false
}
}
fn num_provided_args(&self) -> usize {
if self.missing_lifetimes() {
self.num_provided_lifetime_args()
@ -955,20 +993,26 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
// If there is a single unbound associated type and a single excess generic param
// suggest replacing the generic param with the associated type bound
if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
let suggestions = iter::zip(unused_generics, &unbound_types)
.map(|(potential, name)| (potential.span().shrink_to_lo(), format!("{name} = ")))
.collect::<Vec<_>>();
// Don't suggest if we're in a trait impl as
// that would result in invalid syntax (fixes #116464)
if !self.is_in_trait_impl() {
let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
let suggestions = iter::zip(unused_generics, &unbound_types)
.map(|(potential, name)| {
(potential.span().shrink_to_lo(), format!("{name} = "))
})
.collect::<Vec<_>>();
if !suggestions.is_empty() {
err.multipart_suggestion_verbose(
format!(
"replace the generic bound{s} with the associated type{s}",
s = pluralize!(unbound_types.len())
),
suggestions,
Applicability::MaybeIncorrect,
);
if !suggestions.is_empty() {
err.multipart_suggestion_verbose(
format!(
"replace the generic bound{s} with the associated type{s}",
s = pluralize!(unbound_types.len())
),
suggestions,
Applicability::MaybeIncorrect,
);
}
}
} else if remove_entire_generics {
let span = self

View File

@ -305,7 +305,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) = (parent_node, callee_node)
{
let fn_decl_span = if hir.body(body).coroutine_kind
== Some(hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Closure))
== Some(hir::CoroutineKind::Async(hir::CoroutineSource::Closure))
{
// Actually need to unwrap one more layer of HIR to get to
// the _real_ closure...

View File

@ -660,9 +660,21 @@ impl<'a, 'tcx> CastCheck<'tcx> {
} else {
match self.try_coercion_cast(fcx) {
Ok(()) => {
self.trivial_cast_lint(fcx);
debug!(" -> CoercionCast");
fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id);
if self.expr_ty.is_unsafe_ptr() && self.cast_ty.is_unsafe_ptr() {
// When casting a raw pointer to another raw pointer, we cannot convert the cast into
// a coercion because the pointee types might only differ in regions, which HIR typeck
// cannot distinguish. This would cause us to erroneously discard a cast which will
// lead to a borrowck error like #113257.
// We still did a coercion above to unify inference variables for `ptr as _` casts.
// This does cause us to miss some trivial casts in the trival cast lint.
debug!(" -> PointerCast");
} else {
self.trivial_cast_lint(fcx);
debug!(" -> CoercionCast");
fcx.typeck_results
.borrow_mut()
.set_coercion_cast(self.expr.hir_id.local_id);
}
}
Err(_) => {
match self.do_check(fcx) {

View File

@ -636,7 +636,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// In the case of the async block that we create for a function body,
// we expect the return type of the block to match that of the enclosing
// function.
Some(hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Fn)) => {
Some(hir::CoroutineKind::Async(hir::CoroutineSource::Fn)) => {
debug!("closure is async fn body");
let def_id = self.tcx.hir().body_owner_def_id(body.id());
self.deduce_future_output_from_obligations(expr_def_id, def_id).unwrap_or_else(

View File

@ -962,38 +962,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>,
found: Ty<'tcx>,
) -> bool {
let ty::Adt(e, args_e) = expected.kind() else {
return false;
};
let ty::Adt(f, args_f) = found.kind() else {
return false;
};
if e.did() != f.did() {
return false;
}
if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) {
return false;
}
let map = self.tcx.hir();
if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id)
&& let hir::ExprKind::Ret(_) = expr.kind
{
// `return foo;`
} else if map.get_return_block(expr.hir_id).is_some() {
// Function's tail expression.
} else {
return false;
}
let e = args_e.type_at(1);
let f = args_f.type_at(1);
if self
.infcx
.type_implements_trait(
self.tcx.get_diagnostic_item(sym::Into).unwrap(),
[f, e],
self.param_env,
)
.must_apply_modulo_regions()
let returned = matches!(
map.find_parent(expr.hir_id),
Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }))
) || map.get_return_block(expr.hir_id).is_some();
if returned
&& let ty::Adt(e, args_e) = expected.kind()
&& let ty::Adt(f, args_f) = found.kind()
&& e.did() == f.did()
&& Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
&& let e_ok = args_e.type_at(0)
&& let f_ok = args_f.type_at(0)
&& self.infcx.can_eq(self.param_env, f_ok, e_ok)
&& let e_err = args_e.type_at(1)
&& let f_err = args_f.type_at(1)
&& self
.infcx
.type_implements_trait(
self.tcx.get_diagnostic_item(sym::Into).unwrap(),
[f_err, e_err],
self.param_env,
)
.must_apply_modulo_regions()
{
err.multipart_suggestion(
"use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \

View File

@ -26,7 +26,7 @@ use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{
self, AdtKind, CanonicalUserType, GenericParamDefKind, Ty, TyCtxt, UserType,
self, AdtKind, CanonicalUserType, GenericParamDefKind, IsIdentity, Ty, TyCtxt, UserType,
};
use rustc_middle::ty::{GenericArgKind, GenericArgsRef, UserArgs, UserSelfTy};
use rustc_session::lint;
@ -207,6 +207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
debug!("fcx {}", self.tag());
// FIXME: is_identity being on `UserType` and not `Canonical<UserType>` is awkward
if !canonical_user_type_annotation.is_identity() {
self.typeck_results
.borrow_mut()

View File

@ -10,8 +10,8 @@ use rustc_hir::def::Res;
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{
AsyncCoroutineKind, CoroutineKind, Expr, ExprKind, GenericBound, HirId, Node, Path, QPath,
Stmt, StmtKind, TyKind, WherePredicate,
CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, Path, QPath, Stmt,
StmtKind, TyKind, WherePredicate,
};
use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::traits::{self, StatementAsExpression};
@ -536,7 +536,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Coroutine(def_id, ..)
if matches!(
self.tcx.coroutine_kind(def_id),
Some(CoroutineKind::Async(AsyncCoroutineKind::Closure))
Some(CoroutineKind::Async(CoroutineSource::Closure))
) =>
{
errors::SuggestBoxing::AsyncBody
@ -1747,19 +1747,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>,
found: Ty<'tcx>,
) -> bool {
let ty::Adt(adt, args) = found.kind() else { return false };
let ty::Adt(adt, args) = found.kind() else {
return false;
};
let ret_ty_matches = |diagnostic_item| {
if let Some(ret_ty) = self
.ret_coercion
.as_ref()
.map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
&& let ty::Adt(kind, _) = ret_ty.kind()
&& self.tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
{
true
} else {
false
}
let Some(sig) = self.body_fn_sig() else {
return false;
};
let ty::Adt(kind, _) = sig.output().kind() else {
return false;
};
self.tcx.is_diagnostic_item(diagnostic_item, kind.did())
};
// don't suggest anything like `Ok(ok_val).unwrap()` , `Some(some_val).unwrap()`,

View File

@ -5,7 +5,6 @@
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(never_type)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
@ -19,15 +18,11 @@ mod assert_dep_graph;
mod errors;
mod persist;
use assert_dep_graph::assert_dep_graph;
pub use persist::copy_cgu_workproduct_to_incr_comp_cache_dir;
pub use persist::delete_workproduct_files;
pub use persist::finalize_session_directory;
pub use persist::garbage_collect_session_directories;
pub use persist::in_incr_comp_dir;
pub use persist::in_incr_comp_dir_sess;
pub use persist::load_query_result_cache;
pub use persist::prepare_session_directory;
pub use persist::save_dep_graph;
pub use persist::save_work_product_index;
pub use persist::setup_dep_graph;

View File

@ -53,7 +53,7 @@
//! ## Synchronization
//!
//! There is some synchronization needed in order for the compiler to be able to
//! determine whether a given private session directory is not in used any more.
//! determine whether a given private session directory is not in use any more.
//! This is done by creating a lock file for each session directory and
//! locking it while the directory is still being used. Since file locks have
//! operating system support, we can rely on the lock being released if the
@ -136,26 +136,29 @@ const QUERY_CACHE_FILENAME: &str = "query-cache.bin";
const INT_ENCODE_BASE: usize = base_n::CASE_INSENSITIVE;
/// Returns the path to a session's dependency graph.
pub fn dep_graph_path(sess: &Session) -> PathBuf {
pub(crate) fn dep_graph_path(sess: &Session) -> PathBuf {
in_incr_comp_dir_sess(sess, DEP_GRAPH_FILENAME)
}
/// Returns the path to a session's staging dependency graph.
///
/// On the difference between dep-graph and staging dep-graph,
/// see `build_dep_graph`.
pub fn staging_dep_graph_path(sess: &Session) -> PathBuf {
pub(crate) fn staging_dep_graph_path(sess: &Session) -> PathBuf {
in_incr_comp_dir_sess(sess, STAGING_DEP_GRAPH_FILENAME)
}
pub fn work_products_path(sess: &Session) -> PathBuf {
pub(crate) fn work_products_path(sess: &Session) -> PathBuf {
in_incr_comp_dir_sess(sess, WORK_PRODUCTS_FILENAME)
}
/// Returns the path to a session's query cache.
pub fn query_cache_path(sess: &Session) -> PathBuf {
in_incr_comp_dir_sess(sess, QUERY_CACHE_FILENAME)
}
/// Locks a given session directory.
pub fn lock_file_path(session_dir: &Path) -> PathBuf {
fn lock_file_path(session_dir: &Path) -> PathBuf {
let crate_dir = session_dir.parent().unwrap();
let directory_name = session_dir.file_name().unwrap().to_string_lossy();
@ -202,7 +205,7 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu
/// The garbage collection will take care of it.
///
/// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph
pub fn prepare_session_directory(
pub(crate) fn prepare_session_directory(
sess: &Session,
crate_name: Symbol,
stable_crate_id: StableCrateId,
@ -373,7 +376,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
let _ = garbage_collect_session_directories(sess);
}
pub fn delete_all_session_dir_contents(sess: &Session) -> io::Result<()> {
pub(crate) fn delete_all_session_dir_contents(sess: &Session) -> io::Result<()> {
let sess_dir_iterator = sess.incr_comp_session_dir().read_dir()?;
for entry in sess_dir_iterator {
let entry = entry?;
@ -621,7 +624,7 @@ fn is_old_enough_to_be_collected(timestamp: SystemTime) -> bool {
}
/// Runs garbage collection for the current session.
pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
debug!("garbage_collect_session_directories() - begin");
let session_directory = sess.incr_comp_session_dir();

View File

@ -1,4 +1,4 @@
//! Code to save/load the dep-graph from files.
//! Code to load the dep-graph from files.
use crate::errors;
use rustc_data_structures::memmap::Mmap;

View File

@ -11,14 +11,11 @@ mod save;
mod work_product;
pub use fs::finalize_session_directory;
pub use fs::garbage_collect_session_directories;
pub use fs::in_incr_comp_dir;
pub use fs::in_incr_comp_dir_sess;
pub use fs::prepare_session_directory;
pub use load::load_query_result_cache;
pub use load::setup_dep_graph;
pub use load::LoadResult;
pub use save::save_dep_graph;
pub use save::save_work_product_index;
pub use work_product::copy_cgu_workproduct_to_incr_comp_cache_dir;
pub use work_product::delete_workproduct_files;

View File

@ -1,3 +1,4 @@
use crate::assert_dep_graph::assert_dep_graph;
use crate::errors;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync::join;
@ -39,7 +40,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
let dep_graph_path = dep_graph_path(sess);
let staging_dep_graph_path = staging_dep_graph_path(sess);
sess.time("assert_dep_graph", || crate::assert_dep_graph(tcx));
sess.time("assert_dep_graph", || assert_dep_graph(tcx));
sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx));
if sess.opts.unstable_opts.incremental_info {

View File

@ -11,7 +11,8 @@ use rustc_session::Session;
use std::fs as std_fs;
use std::path::Path;
/// Copies a CGU work product to the incremental compilation directory, so next compilation can find and reuse it.
/// Copies a CGU work product to the incremental compilation directory, so next compilation can
/// find and reuse it.
pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
sess: &Session,
cgu_name: &str,
@ -45,7 +46,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
}
/// Removes files for a given work product.
pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) {
pub(crate) fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) {
for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() {
let path = in_incr_comp_dir_sess(sess, path);
if let Err(err) = std_fs::remove_file(&path) {

View File

@ -1584,14 +1584,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
target: &str,
types: &FxIndexMap<TyCategory, FxIndexSet<Span>>,
) {
for (key, values) in types.iter() {
for (kind, values) in types.iter() {
let count = values.len();
let kind = key.descr();
for &sp in values {
err.span_label(
sp,
format!(
"{}{} {}{}",
"{}{} {:#}{}",
if count == 1 { "the " } else { "one of the " },
target,
kind,
@ -2952,17 +2951,19 @@ pub enum TyCategory {
Foreign,
}
impl TyCategory {
fn descr(&self) -> &'static str {
impl fmt::Display for TyCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Closure => "closure",
Self::Opaque => "opaque type",
Self::OpaqueFuture => "future",
Self::Coroutine(gk) => gk.descr(),
Self::Foreign => "foreign type",
Self::Closure => "closure".fmt(f),
Self::Opaque => "opaque type".fmt(f),
Self::OpaqueFuture => "future".fmt(f),
Self::Coroutine(gk) => gk.fmt(f),
Self::Foreign => "foreign type".fmt(f),
}
}
}
impl TyCategory {
pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> {
match *ty.kind() {
ty::Closure(def_id, _) => Some((Self::Closure, def_id)),

View File

@ -54,13 +54,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
(ty::Param(expected), ty::Param(found)) => {
let generics = tcx.generics_of(body_owner_def_id);
let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
if !sp.contains(e_span) {
diag.span_label(e_span, "expected type parameter");
if let Some(param) = generics.opt_type_param(expected, tcx) {
let e_span = tcx.def_span(param.def_id);
if !sp.contains(e_span) {
diag.span_label(e_span, "expected type parameter");
}
}
let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
if !sp.contains(f_span) {
diag.span_label(f_span, "found type parameter");
if let Some(param) = generics.opt_type_param(found, tcx) {
let f_span = tcx.def_span(param.def_id);
if !sp.contains(f_span) {
diag.span_label(f_span, "found type parameter");
}
}
diag.note(
"a type parameter was expected, but a different one was found; \
@ -83,23 +87,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
| (ty::Alias(ty::Projection, proj), ty::Param(p))
if !tcx.is_impl_trait_in_trait(proj.def_id) =>
{
let p_def_id = tcx.generics_of(body_owner_def_id).type_param(p, tcx).def_id;
let p_span = tcx.def_span(p_def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(p_span, format!("{expected}this type parameter"));
}
let hir = tcx.hir();
let parent = tcx.generics_of(body_owner_def_id)
.opt_type_param(p, tcx)
.and_then(|param| {
let p_def_id = param.def_id;
let p_span = tcx.def_span(p_def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(
p_span,
format!("{expected}this type parameter"),
);
}
p_def_id.as_local().and_then(|id| {
let local_id = tcx.hir().local_def_id_to_hir_id(id);
let generics = tcx.hir().find_parent(local_id)?.generics()?;
Some((id, generics))
})
});
let mut note = true;
let parent = p_def_id.as_local().and_then(|id| {
let local_id = hir.local_def_id_to_hir_id(id);
let generics = tcx.hir().find_parent(local_id)?.generics()?;
Some((id, generics))
});
if let Some((local_id, generics)) = parent {
// Synthesize the associated type restriction `Add<Output = Expected>`.
// FIXME: extract this logic for use in other diagnostics.
@ -172,14 +182,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
(ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
| (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(p_span, format!("{expected}this type parameter"));
if let Some(param) = generics.opt_type_param(p, tcx) {
let p_span = tcx.def_span(param.def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(p_span, format!("{expected}this type parameter"));
}
}
diag.help("type parameters must be constrained to match other types");
if tcx.sess.teach(&diag.get_code().unwrap()) {
@ -217,9 +229,11 @@ impl<T> Trait<T> for X {
}
(ty::Param(p), ty::Closure(..) | ty::Coroutine(..)) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "expected this type parameter");
if let Some(param) = generics.opt_type_param(p, tcx) {
let p_span = tcx.def_span(param.def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "expected this type parameter");
}
}
diag.help(format!(
"every closure has a distinct type and so could not always match the \
@ -228,14 +242,16 @@ impl<T> Trait<T> for X {
}
(ty::Param(p), _) | (_, ty::Param(p)) => {
let generics = tcx.generics_of(body_owner_def_id);
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(p_span, format!("{expected}this type parameter"));
if let Some(param) = generics.opt_type_param(p, tcx) {
let p_span = tcx.def_span(param.def_id);
let expected = match (values.expected.kind(), values.found.kind()) {
(ty::Param(_), _) => "expected ",
(_, ty::Param(_)) => "found ",
_ => "",
};
if !sp.contains(p_span) {
diag.span_label(p_span, format!("{expected}this type parameter"));
}
}
}
(ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
@ -364,13 +380,14 @@ impl<T> Trait<T> for X {
};
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
// This will also work for `impl Trait`.
let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
let generics = tcx.generics_of(body_owner_def_id);
generics.type_param(param_ty, tcx).def_id
} else {
let ty::Param(param_ty) = proj_ty.self_ty().kind() else {
return false;
};
let Some(def_id) = def_id.as_local() else {
let generics = tcx.generics_of(body_owner_def_id);
let Some(param) = generics.opt_type_param(param_ty, tcx) else {
return false;
};
let Some(def_id) = param.def_id.as_local() else {
return false;
};
@ -390,6 +407,10 @@ impl<T> Trait<T> for X {
return true;
}
}
if (param_ty.index as usize) >= generics.parent_count {
// The param comes from the current item, do not look at the parent. (#117209)
return false;
}
// If associated item, look to constrain the params of the trait/impl.
let hir_id = match item {
hir::Node::ImplItem(item) => item.hir_id(),

View File

@ -24,6 +24,7 @@ use rustc_span::source_map::{FileLoader, FileName};
use rustc_span::symbol::sym;
use std::path::PathBuf;
use std::result;
use std::sync::Arc;
pub type Result<T> = result::Result<T, ErrorGuaranteed>;
@ -410,6 +411,12 @@ pub struct Config {
/// Registry of diagnostics codes.
pub registry: Registry,
/// The inner atomic value is set to true when a feature marked as `internal` is
/// enabled. Makes it so that "please report a bug" is hidden, as ICEs with
/// internal features are wontfix, and they are usually the cause of the ICEs.
/// None signifies that this is not tracked.
pub using_internal_features: Arc<std::sync::atomic::AtomicBool>,
/// All commandline args used to invoke the compiler, with @file args fully expanded.
/// This will only be used within debug info, e.g. in the pdb file on windows
/// This is mainly useful for other tools that reads that debuginfo to figure out
@ -453,6 +460,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
config.make_codegen_backend,
registry.clone(),
config.ice_file,
config.using_internal_features,
config.expanded_args,
);

View File

@ -856,6 +856,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
// This check has to be run after all lints are done processing. We don't
// define a lint filter, as all lint checks should have finished at this point.
sess.time("check_lint_expectations", || tcx.ensure().check_expectations(None));
// This query is only invoked normally if a diagnostic is emitted that needs any
// diagnostic item. If the crate compiles without checking any diagnostic items,
// we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally.
let _ = tcx.all_diagnostic_items(());
});
if sess.opts.unstable_opts.print_vtable_sizes {

View File

@ -181,9 +181,11 @@ impl<'tcx> Queries<'tcx> {
feed.crate_name(crate_name);
let feed = tcx.feed_unit_query();
feed.features_query(
tcx.arena.alloc(rustc_expand::config::features(sess, &pre_configured_attrs)),
);
feed.features_query(tcx.arena.alloc(rustc_expand::config::features(
sess,
&pre_configured_attrs,
crate_name,
)));
feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs))));
});
Ok(qcx)

View File

@ -35,6 +35,7 @@ use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtecto
use std::collections::{BTreeMap, BTreeSet};
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
use std::sync::Arc;
type CfgSpecs = FxHashSet<(String, Option<String>)>;
@ -69,6 +70,7 @@ fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Se
None,
"",
None,
Arc::default(),
Default::default(),
);
(sess, cfg)
@ -612,7 +614,7 @@ fn test_codegen_options_tracking_hash() {
tracked!(force_frame_pointers, Some(false));
tracked!(force_unwind_tables, Some(true));
tracked!(inline_threshold, Some(0xf007ba11));
tracked!(instrument_coverage, Some(InstrumentCoverage::All));
tracked!(instrument_coverage, InstrumentCoverage::All);
tracked!(link_dead_code, Some(true));
tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
tracked!(llvm_args, vec![String::from("1"), String::from("2")]);
@ -789,7 +791,6 @@ fn test_unstable_options_tracking_hash() {
tracked!(inline_mir, Some(true));
tracked!(inline_mir_hint_threshold, Some(123));
tracked!(inline_mir_threshold, Some(123));
tracked!(instrument_coverage, Some(InstrumentCoverage::All));
tracked!(instrument_mcount, true);
tracked!(instrument_xray, Some(InstrumentXRay::default()));
tracked!(link_directives, false);

View File

@ -26,7 +26,7 @@ use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::mem;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::OnceLock;
use std::sync::{Arc, OnceLock};
use std::thread;
/// Function pointer type that constructs a new CodegenBackend.
@ -71,6 +71,7 @@ pub fn create_session(
>,
descriptions: Registry,
ice_file: Option<PathBuf>,
using_internal_features: Arc<AtomicBool>,
expanded_args: Vec<String>,
) -> (Session, Box<dyn CodegenBackend>) {
let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend {
@ -114,6 +115,7 @@ pub fn create_session(
target_override,
rustc_version_str().unwrap_or("unknown"),
ice_file,
using_internal_features,
expanded_args,
);

View File

@ -4,6 +4,7 @@
#![feature(never_type)]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_span)]
#![feature(proc_macro_tracked_env)]
#![allow(rustc::default_hash_types)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

View File

@ -97,6 +97,9 @@ struct QueryModifiers {
/// A cycle error results in a delay_bug call
cycle_delay_bug: Option<Ident>,
/// A cycle error results in a stashed cycle error that can be unstashed and canceled later
cycle_stash: Option<Ident>,
/// Don't hash the result, instead just mark a query red if it runs
no_hash: Option<Ident>,
@ -127,6 +130,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
let mut desc = None;
let mut fatal_cycle = None;
let mut cycle_delay_bug = None;
let mut cycle_stash = None;
let mut no_hash = None;
let mut anon = None;
let mut eval_always = None;
@ -181,6 +185,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
try_insert!(fatal_cycle = modifier);
} else if modifier == "cycle_delay_bug" {
try_insert!(cycle_delay_bug = modifier);
} else if modifier == "cycle_stash" {
try_insert!(cycle_stash = modifier);
} else if modifier == "no_hash" {
try_insert!(no_hash = modifier);
} else if modifier == "anon" {
@ -208,6 +214,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
desc,
fatal_cycle,
cycle_delay_bug,
cycle_stash,
no_hash,
anon,
eval_always,
@ -329,6 +336,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
fatal_cycle,
arena_cache,
cycle_delay_bug,
cycle_stash,
no_hash,
anon,
eval_always,

View File

@ -26,7 +26,7 @@ use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream, Result};
use syn::{braced, punctuated::Punctuated, Ident, LitStr, Token};
use syn::{braced, punctuated::Punctuated, Expr, Ident, Lit, LitStr, Macro, Token};
#[cfg(test)]
mod tests;
@ -53,21 +53,46 @@ impl Parse for Keyword {
struct Symbol {
name: Ident,
value: Option<LitStr>,
value: Value,
}
enum Value {
SameAsName,
String(LitStr),
Env(LitStr, Macro),
Unsupported(Expr),
}
impl Parse for Symbol {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let name = input.parse()?;
let value = match input.parse::<Token![:]>() {
Ok(_) => Some(input.parse()?),
Err(_) => None,
};
let colon_token: Option<Token![:]> = input.parse()?;
let value = if colon_token.is_some() { input.parse()? } else { Value::SameAsName };
Ok(Symbol { name, value })
}
}
impl Parse for Value {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let expr: Expr = input.parse()?;
match &expr {
Expr::Lit(expr) => {
if let Lit::Str(lit) = &expr.lit {
return Ok(Value::String(lit.clone()));
}
}
Expr::Macro(expr) => {
if expr.mac.path.is_ident("env") && let Ok(lit) = expr.mac.parse_body() {
return Ok(Value::Env(lit, expr.mac.clone()));
}
}
_ => {}
}
Ok(Value::Unsupported(expr))
}
}
struct Input {
keywords: Punctuated<Keyword, Token![,]>,
symbols: Punctuated<Symbol, Token![,]>,
@ -111,6 +136,37 @@ pub fn symbols(input: TokenStream) -> TokenStream {
output
}
struct Preinterned {
idx: u32,
span_of_name: Span,
}
struct Entries {
map: HashMap<String, Preinterned>,
}
impl Entries {
fn with_capacity(capacity: usize) -> Self {
Entries { map: HashMap::with_capacity(capacity) }
}
fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 {
if let Some(prev) = self.map.get(str) {
errors.error(span, format!("Symbol `{str}` is duplicated"));
errors.error(prev.span_of_name, "location of previous definition".to_string());
prev.idx
} else {
let idx = self.len();
self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span });
idx
}
}
fn len(&self) -> u32 {
u32::try_from(self.map.len()).expect("way too many symbols")
}
}
fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
let mut errors = Errors::default();
@ -127,20 +183,9 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
let mut keyword_stream = quote! {};
let mut symbols_stream = quote! {};
let mut prefill_stream = quote! {};
let mut counter = 0u32;
let mut keys =
HashMap::<String, Span>::with_capacity(input.keywords.len() + input.symbols.len() + 10);
let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10);
let mut prev_key: Option<(Span, String)> = None;
let mut check_dup = |span: Span, str: &str, errors: &mut Errors| {
if let Some(prev_span) = keys.get(str) {
errors.error(span, format!("Symbol `{str}` is duplicated"));
errors.error(*prev_span, "location of previous definition".to_string());
} else {
keys.insert(str.to_string(), span);
}
};
let mut check_order = |span: Span, str: &str, errors: &mut Errors| {
if let Some((prev_span, ref prev_str)) = prev_key {
if str < prev_str {
@ -156,49 +201,98 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
let name = &keyword.name;
let value = &keyword.value;
let value_string = value.value();
check_dup(keyword.name.span(), &value_string, &mut errors);
let idx = entries.insert(keyword.name.span(), &value_string, &mut errors);
prefill_stream.extend(quote! {
#value,
});
keyword_stream.extend(quote! {
pub const #name: Symbol = Symbol::new(#counter);
pub const #name: Symbol = Symbol::new(#idx);
});
counter += 1;
}
// Generate the listed symbols.
for symbol in input.symbols.iter() {
let name = &symbol.name;
let value = match &symbol.value {
Some(value) => value.value(),
None => name.to_string(),
};
check_dup(symbol.name.span(), &value, &mut errors);
check_order(symbol.name.span(), &name.to_string(), &mut errors);
let value = match &symbol.value {
Value::SameAsName => name.to_string(),
Value::String(lit) => lit.value(),
Value::Env(..) => continue, // in another loop below
Value::Unsupported(expr) => {
errors.list.push(syn::Error::new_spanned(
expr,
concat!(
"unsupported expression for symbol value; implement support for this in ",
file!(),
),
));
continue;
}
};
let idx = entries.insert(symbol.name.span(), &value, &mut errors);
prefill_stream.extend(quote! {
#value,
});
symbols_stream.extend(quote! {
pub const #name: Symbol = Symbol::new(#counter);
pub const #name: Symbol = Symbol::new(#idx);
});
counter += 1;
}
// Generate symbols for the strings "0", "1", ..., "9".
let digits_base = counter;
counter += 10;
for n in 0..10 {
let n = n.to_string();
check_dup(Span::call_site(), &n, &mut errors);
entries.insert(Span::call_site(), &n, &mut errors);
prefill_stream.extend(quote! {
#n,
});
}
// Symbols whose value comes from an environment variable. It's allowed for
// these to have the same value as another symbol.
for symbol in &input.symbols {
let (env_var, expr) = match &symbol.value {
Value::Env(lit, expr) => (lit, expr),
Value::SameAsName | Value::String(_) | Value::Unsupported(_) => continue,
};
if !proc_macro::is_available() {
errors.error(
Span::call_site(),
"proc_macro::tracked_env is not available in unit test".to_owned(),
);
break;
}
let value = match proc_macro::tracked_env::var(env_var.value()) {
Ok(value) => value,
Err(err) => {
errors.list.push(syn::Error::new_spanned(expr, err));
continue;
}
};
let idx = if let Some(prev) = entries.map.get(&value) {
prev.idx
} else {
prefill_stream.extend(quote! {
#value,
});
entries.insert(symbol.name.span(), &value, &mut errors)
};
let name = &symbol.name;
symbols_stream.extend(quote! {
pub const #name: Symbol = Symbol::new(#idx);
});
}
let symbol_digits_base = entries.map["0"].idx;
let preinterned_symbols_count = entries.len();
let output = quote! {
const SYMBOL_DIGITS_BASE: u32 = #digits_base;
const PREINTERNED_SYMBOLS_COUNT: u32 = #counter;
const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base;
const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count;
#[doc(hidden)]
#[allow(non_upper_case_globals)]

View File

@ -27,7 +27,7 @@ fn test_symbols() {
let body_tokens = m.mac.tokens.clone();
test_symbols_macro(body_tokens, &[]);
test_symbols_macro(body_tokens, &["proc_macro::tracked_env is not available in unit test"]);
}
fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) {

View File

@ -21,35 +21,17 @@
//!
//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
use rustc_macros::HashStable;
use rustc_type_ir::Canonical as IrCanonical;
use smallvec::SmallVec;
use std::ops::Index;
use crate::infer::MemberConstraint;
use crate::mir::ConstraintCategory;
use crate::ty::GenericArg;
use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt};
use rustc_macros::HashStable;
use smallvec::SmallVec;
use std::fmt::Display;
use std::ops::Index;
/// A "canonicalized" type `V` is one where all free inference
/// variables have been rewritten to "canonical vars". These are
/// numbered starting from 0 in order of first appearance.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable)]
pub struct Canonical<'tcx, V> {
pub value: V,
pub max_universe: ty::UniverseIndex,
pub variables: CanonicalVarInfos<'tcx>,
}
impl<'tcx, V: Display> std::fmt::Display for Canonical<'tcx, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}",
self.value, self.max_universe, self.variables
)
}
}
pub type Canonical<'tcx, V> = IrCanonical<TyCtxt<'tcx>, V>;
pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;
@ -379,56 +361,6 @@ impl<'tcx, R> QueryResponse<'tcx, R> {
}
}
impl<'tcx, R> Canonical<'tcx, QueryResponse<'tcx, R>> {
pub fn is_proven(&self) -> bool {
self.value.is_proven()
}
pub fn is_ambiguous(&self) -> bool {
!self.is_proven()
}
}
impl<'tcx, V> Canonical<'tcx, V> {
/// Allows you to map the `value` of a canonical while keeping the
/// same set of bound variables.
///
/// **WARNING:** This function is very easy to mis-use, hence the
/// name! In particular, the new value `W` must use all **the
/// same type/region variables** in **precisely the same order**
/// as the original! (The ordering is defined by the
/// `TypeFoldable` implementation of the type in question.)
///
/// An example of a **correct** use of this:
///
/// ```rust,ignore (not real code)
/// let a: Canonical<'_, T> = ...;
/// let b: Canonical<'_, (T,)> = a.unchecked_map(|v| (v, ));
/// ```
///
/// An example of an **incorrect** use of this:
///
/// ```rust,ignore (not real code)
/// let a: Canonical<'tcx, T> = ...;
/// let ty: Ty<'tcx> = ...;
/// let b: Canonical<'tcx, (T, Ty<'tcx>)> = a.unchecked_map(|v| (v, ty));
/// ```
pub fn unchecked_map<W>(self, map_op: impl FnOnce(V) -> W) -> Canonical<'tcx, W> {
let Canonical { max_universe, variables, value } = self;
Canonical { max_universe, variables, value: map_op(value) }
}
/// Allows you to map the `value` of a canonical while keeping the same set of
/// bound variables.
///
/// **WARNING:** This function is very easy to mis-use, hence the name! See
/// the comment of [Canonical::unchecked_map] for more details.
pub fn unchecked_rebind<W>(self, value: W) -> Canonical<'tcx, W> {
let Canonical { max_universe, variables, value: _ } = self;
Canonical { max_universe, variables, value }
}
}
pub type QueryOutlivesConstraint<'tcx> =
(ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>, ConstraintCategory<'tcx>);

View File

@ -251,6 +251,7 @@ rustc_queries! {
"computing type of opaque `{path}`",
path = tcx.def_path_str(key),
}
cycle_stash
}
query type_alias_is_lazy(key: DefId) -> bool {
@ -341,7 +342,7 @@ rustc_queries! {
query opaque_types_defined_by(
key: LocalDefId
) -> &'tcx [LocalDefId] {
) -> &'tcx ty::List<LocalDefId> {
desc {
|tcx| "computing the opaque types defined by `{}`",
tcx.def_path_str(key.to_def_id())

View File

@ -230,9 +230,9 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D>
assert!(pos >= SHORTHAND_OFFSET);
let shorthand = pos - SHORTHAND_OFFSET;
decoder.with_position(shorthand, ty::PredicateKind::decode)
decoder.with_position(shorthand, <ty::PredicateKind<'tcx> as Decodable<D>>::decode)
} else {
ty::PredicateKind::decode(decoder)
<ty::PredicateKind<'tcx> as Decodable<D>>::decode(decoder)
},
bound_vars,
)

View File

@ -6,7 +6,7 @@ pub mod tls;
use crate::arena::Arena;
use crate::dep_graph::{DepGraph, DepKindStruct};
use crate::infer::canonical::CanonicalVarInfo;
use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::struct_lint_level;
use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
@ -84,10 +84,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type AdtDef = ty::AdtDef<'tcx>;
type GenericArgs = ty::GenericArgsRef<'tcx>;
type GenericArg = ty::GenericArg<'tcx>;
type Term = ty::Term<'tcx>;
type Binder<T> = Binder<'tcx, T>;
type Predicate = Predicate<'tcx>;
type PredicateKind = ty::PredicateKind<'tcx>;
type TypeAndMut = TypeAndMut<'tcx>;
type CanonicalVars = CanonicalVarInfos<'tcx>;
type Ty = Ty<'tcx>;
type Tys = &'tcx List<Ty<'tcx>>;
type AliasTy = ty::AliasTy<'tcx>;
@ -95,10 +97,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type BoundTy = ty::BoundTy;
type PlaceholderTy = ty::PlaceholderType;
type InferTy = InferTy;
type ErrorGuaranteed = ErrorGuaranteed;
type BoundExistentialPredicates = &'tcx List<PolyExistentialPredicate<'tcx>>;
type PolyFnSig = PolyFnSig<'tcx>;
type AllocId = crate::mir::interpret::AllocId;
type Const = ty::Const<'tcx>;
type InferConst = ty::InferConst;
type AliasConst = ty::UnevaluatedConst<'tcx>;
@ -107,6 +111,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type BoundConst = ty::BoundVar;
type ValueConst = ty::ValTree<'tcx>;
type ExprConst = ty::Expr<'tcx>;
type Region = Region<'tcx>;
type EarlyBoundRegion = ty::EarlyBoundRegion;
type BoundRegion = ty::BoundRegion;
@ -114,6 +119,15 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type InferRegion = ty::RegionVid;
type PlaceholderRegion = ty::PlaceholderRegion;
type Predicate = Predicate<'tcx>;
type TraitPredicate = ty::TraitPredicate<'tcx>;
type RegionOutlivesPredicate = ty::RegionOutlivesPredicate<'tcx>;
type TypeOutlivesPredicate = ty::TypeOutlivesPredicate<'tcx>;
type ProjectionPredicate = ty::ProjectionPredicate<'tcx>;
type SubtypePredicate = ty::SubtypePredicate<'tcx>;
type CoercePredicate = ty::CoercePredicate<'tcx>;
type ClosureKind = ty::ClosureKind;
fn ty_and_mut_to_parts(
TypeAndMut { ty, mutbl }: TypeAndMut<'tcx>,
) -> (Self::Ty, ty::Mutability) {
@ -148,6 +162,7 @@ pub struct CtxtInterners<'tcx> {
external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>,
fields: InternedSet<'tcx, List<FieldIdx>>,
local_def_ids: InternedSet<'tcx, List<LocalDefId>>,
}
impl<'tcx> CtxtInterners<'tcx> {
@ -173,6 +188,7 @@ impl<'tcx> CtxtInterners<'tcx> {
external_constraints: Default::default(),
predefined_opaques_in_body: Default::default(),
fields: Default::default(),
local_def_ids: Default::default(),
}
}
@ -1559,6 +1575,7 @@ slice_interners!(
place_elems: pub mk_place_elems(PlaceElem<'tcx>),
bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
fields: pub mk_fields(FieldIdx),
local_def_ids: intern_local_def_ids(LocalDefId),
);
impl<'tcx> TyCtxt<'tcx> {
@ -1788,6 +1805,13 @@ impl<'tcx> TyCtxt<'tcx> {
self.intern_clauses(clauses)
}
pub fn mk_local_def_ids(self, clauses: &[LocalDefId]) -> &'tcx List<LocalDefId> {
// FIXME consider asking the input slice to be sorted to avoid
// re-interning permutations, in which case that would be asserted
// here.
self.intern_local_def_ids(clauses)
}
pub fn mk_const_list_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,

View File

@ -241,7 +241,9 @@ impl<'tcx> Ty<'tcx> {
}
ty::Dynamic(..) => "trait object".into(),
ty::Closure(..) => "closure".into(),
ty::Coroutine(def_id, ..) => tcx.coroutine_kind(def_id).unwrap().descr().into(),
ty::Coroutine(def_id, ..) => {
format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into()
}
ty::CoroutineWitness(..) => "coroutine witness".into(),
ty::Infer(ty::TyVar(_)) => "inferred type".into(),
ty::Infer(ty::IntVar(_)) => "integer".into(),
@ -299,7 +301,9 @@ impl<'tcx> Ty<'tcx> {
ty::FnPtr(_) => "fn pointer".into(),
ty::Dynamic(..) => "trait object".into(),
ty::Closure(..) => "closure".into(),
ty::Coroutine(def_id, ..) => tcx.coroutine_kind(def_id).unwrap().descr().into(),
ty::Coroutine(def_id, ..) => {
format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into()
}
ty::CoroutineWitness(..) => "coroutine witness".into(),
ty::Tuple(..) => "tuple".into(),
ty::Placeholder(..) => "higher-ranked type".into(),

View File

@ -134,7 +134,7 @@ impl FlagComputation {
if should_remove_further_specializable {
self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE;
}
self.add_flags(TypeFlags::HAS_TY_GENERATOR);
self.add_flags(TypeFlags::HAS_TY_COROUTINE);
}
&ty::Closure(_, args) => {

View File

@ -237,6 +237,20 @@ impl<'tcx> Generics {
}
}
/// Returns the `GenericParamDef` with the given index if available.
pub fn opt_param_at(
&'tcx self,
param_index: usize,
tcx: TyCtxt<'tcx>,
) -> Option<&'tcx GenericParamDef> {
if let Some(index) = param_index.checked_sub(self.parent_count) {
self.params.get(index)
} else {
tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?"))
.opt_param_at(param_index, tcx)
}
}
pub fn params_to(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx [GenericParamDef] {
if let Some(index) = param_index.checked_sub(self.parent_count) {
&self.params[..index]
@ -268,6 +282,20 @@ impl<'tcx> Generics {
}
}
/// Returns the `GenericParamDef` associated with this `ParamTy` if it belongs to this
/// `Generics`.
pub fn opt_type_param(
&'tcx self,
param: &ParamTy,
tcx: TyCtxt<'tcx>,
) -> Option<&'tcx GenericParamDef> {
let param = self.opt_param_at(param.index as usize, tcx)?;
match param.kind {
GenericParamDefKind::Type { .. } => Some(param),
_ => None,
}
}
/// Returns the `GenericParamDef` associated with this `ParamConst`.
pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef {
let param = self.param_at(param.index as usize, tcx);

View File

@ -97,17 +97,17 @@ pub use self::rvalue_scopes::RvalueScopes;
pub use self::sty::BoundRegionKind::*;
pub use self::sty::{
AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar,
BoundVariableKind, CanonicalPolyFnSig, ClosureArgs, ClosureArgsParts, ConstKind, ConstVid,
CoroutineArgs, CoroutineArgsParts, EarlyBoundRegion, EffectVid, ExistentialPredicate,
BoundVariableKind, CanonicalPolyFnSig, ClauseKind, ClosureArgs, ClosureArgsParts, ConstKind,
ConstVid, CoroutineArgs, CoroutineArgsParts, EarlyBoundRegion, EffectVid, ExistentialPredicate,
ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, InlineConstArgs,
InlineConstArgsParts, ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection,
PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, Region, RegionKind, RegionVid,
TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo,
PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, PredicateKind, Region,
RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo,
};
pub use self::trait_def::TraitDef;
pub use self::typeck_results::{
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, TypeckResults,
UserType, UserTypeAnnotationIndex,
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity,
TypeckResults, UserType, UserTypeAnnotationIndex,
};
pub mod _match;
@ -233,6 +233,7 @@ impl MainDefinition {
#[derive(Clone, Debug, TypeFoldable, TypeVisitable)]
pub struct ImplHeader<'tcx> {
pub impl_def_id: DefId,
pub impl_args: ty::GenericArgsRef<'tcx>,
pub self_ty: Ty<'tcx>,
pub trait_ref: Option<TraitRef<'tcx>>,
pub predicates: Vec<Predicate<'tcx>>,
@ -626,98 +627,6 @@ impl<'tcx> Clause<'tcx> {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
/// A clause is something that can appear in where bounds or be inferred
/// by implied bounds.
pub enum ClauseKind<'tcx> {
/// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be
/// the `Self` type of the trait reference and `A`, `B`, and `C`
/// would be the type parameters.
Trait(TraitPredicate<'tcx>),
/// `where 'a: 'b`
RegionOutlives(RegionOutlivesPredicate<'tcx>),
/// `where T: 'a`
TypeOutlives(TypeOutlivesPredicate<'tcx>),
/// `where <T as TraitRef>::Name == X`, approximately.
/// See the `ProjectionPredicate` struct for details.
Projection(ProjectionPredicate<'tcx>),
/// Ensures that a const generic argument to a parameter `const N: u8`
/// is of type `u8`.
ConstArgHasType(Const<'tcx>, Ty<'tcx>),
/// No syntax: `T` well-formed.
WellFormed(GenericArg<'tcx>),
/// Constant initializer must evaluate successfully.
ConstEvaluatable(ty::Const<'tcx>),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
pub enum PredicateKind<'tcx> {
/// Prove a clause
Clause(ClauseKind<'tcx>),
/// Trait must be object-safe.
ObjectSafe(DefId),
/// No direct syntax. May be thought of as `where T: FnFoo<...>`
/// for some generic args `...` and `T` being a closure type.
/// Satisfied (or refuted) once we know the closure's kind.
ClosureKind(DefId, GenericArgsRef<'tcx>, ClosureKind),
/// `T1 <: T2`
///
/// This obligation is created most often when we have two
/// unresolved type variables and hence don't have enough
/// information to process the subtyping obligation yet.
Subtype(SubtypePredicate<'tcx>),
/// `T1` coerced to `T2`
///
/// Like a subtyping obligation, this is created most often
/// when we have two unresolved type variables and hence
/// don't have enough information to process the coercion
/// obligation yet. At the moment, we actually process coercions
/// very much like subtyping and don't handle the full coercion
/// logic.
Coerce(CoercePredicate<'tcx>),
/// Constants must be equal. The first component is the const that is expected.
ConstEquate(Const<'tcx>, Const<'tcx>),
/// A marker predicate that is always ambiguous.
/// Used for coherence to mark opaque types as possibly equal to each other but ambiguous.
Ambiguous,
/// Separate from `ClauseKind::Projection` which is used for normalization in new solver.
/// This predicate requires two terms to be equal to eachother.
///
/// Only used for new solver
AliasRelate(Term<'tcx>, Term<'tcx>, AliasRelationDirection),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, Debug)]
pub enum AliasRelationDirection {
Equate,
Subtype,
}
impl std::fmt::Display for AliasRelationDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AliasRelationDirection::Equate => write!(f, "=="),
AliasRelationDirection::Subtype => write!(f, "<:"),
}
}
}
/// The crate outlives map is computed during typeck and contains the
/// outlives of every item in the local crate. You should not use it
/// directly, because to do so will make your pass dependent on the

View File

@ -2649,6 +2649,13 @@ macro_rules! forward_display_to_print {
}
macro_rules! define_print_and_forward_display {
(($self:ident, $cx:ident): $($ty:ty $print:block)+) => {
define_print!(($self, $cx): $($ty $print)*);
forward_display_to_print!($($ty),+);
};
}
macro_rules! define_print {
(($self:ident, $cx:ident): $($ty:ty $print:block)+) => {
$(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty {
fn print(&$self, $cx: &mut P) -> Result<(), PrintError> {
@ -2660,8 +2667,6 @@ macro_rules! define_print_and_forward_display {
Ok(())
}
})+
forward_display_to_print!($($ty),+);
};
}
@ -2759,6 +2764,51 @@ forward_display_to_print! {
ty::OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>
}
define_print! {
(self, cx):
ty::ClauseKind<'tcx> {
match *self {
ty::ClauseKind::Trait(ref data) => {
p!(print(data))
}
ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)),
ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)),
ty::ClauseKind::Projection(predicate) => p!(print(predicate)),
ty::ClauseKind::ConstArgHasType(ct, ty) => {
p!("the constant `", print(ct), "` has type `", print(ty), "`")
},
ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"),
ty::ClauseKind::ConstEvaluatable(ct) => {
p!("the constant `", print(ct), "` can be evaluated")
}
}
}
ty::PredicateKind<'tcx> {
match *self {
ty::PredicateKind::Clause(data) => {
p!(print(data))
}
ty::PredicateKind::Subtype(predicate) => p!(print(predicate)),
ty::PredicateKind::Coerce(predicate) => p!(print(predicate)),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe")
}
ty::PredicateKind::ClosureKind(closure_def_id, _closure_args, kind) => p!(
"the closure `",
print_value_path(closure_def_id, &[]),
write("` implements the trait `{}`", kind)
),
ty::PredicateKind::ConstEquate(c1, c2) => {
p!("the constant `", print(c1), "` equals `", print(c2), "`")
}
ty::PredicateKind::Ambiguous => p!("ambiguous"),
ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)),
}
}
}
define_print_and_forward_display! {
(self, cx):
@ -2887,55 +2937,13 @@ define_print_and_forward_display! {
}
ty::Predicate<'tcx> {
let binder = self.kind();
p!(print(binder))
p!(print(self.kind()))
}
ty::Clause<'tcx> {
p!(print(self.kind()))
}
ty::ClauseKind<'tcx> {
match *self {
ty::ClauseKind::Trait(ref data) => {
p!(print(data))
}
ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)),
ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)),
ty::ClauseKind::Projection(predicate) => p!(print(predicate)),
ty::ClauseKind::ConstArgHasType(ct, ty) => {
p!("the constant `", print(ct), "` has type `", print(ty), "`")
},
ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"),
ty::ClauseKind::ConstEvaluatable(ct) => {
p!("the constant `", print(ct), "` can be evaluated")
}
}
}
ty::PredicateKind<'tcx> {
match *self {
ty::PredicateKind::Clause(data) => {
p!(print(data))
}
ty::PredicateKind::Subtype(predicate) => p!(print(predicate)),
ty::PredicateKind::Coerce(predicate) => p!(print(predicate)),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe")
}
ty::PredicateKind::ClosureKind(closure_def_id, _closure_args, kind) => p!(
"the closure `",
print_value_path(closure_def_id, &[]),
write("` implements the trait `{}`", kind)
),
ty::PredicateKind::ConstEquate(c1, c2) => {
p!("the constant `", print(c1), "` equals `", print(c2), "`")
}
ty::PredicateKind::Ambiguous => p!("ambiguous"),
ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)),
}
}
GenericArg<'tcx> {
match self.unpack() {
GenericArgKind::Lifetime(lt) => p!(print(lt)),

View File

@ -185,43 +185,6 @@ impl<'tcx> fmt::Debug for ty::Clause<'tcx> {
}
}
impl<'tcx> fmt::Debug for ty::ClauseKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ty::ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"),
ty::ClauseKind::Trait(ref a) => a.fmt(f),
ty::ClauseKind::RegionOutlives(ref pair) => pair.fmt(f),
ty::ClauseKind::TypeOutlives(ref pair) => pair.fmt(f),
ty::ClauseKind::Projection(ref pair) => pair.fmt(f),
ty::ClauseKind::WellFormed(ref data) => write!(f, "WellFormed({data:?})"),
ty::ClauseKind::ConstEvaluatable(ct) => {
write!(f, "ConstEvaluatable({ct:?})")
}
}
}
}
impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ty::PredicateKind::Clause(ref a) => a.fmt(f),
ty::PredicateKind::Subtype(ref pair) => pair.fmt(f),
ty::PredicateKind::Coerce(ref pair) => pair.fmt(f),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
write!(f, "ObjectSafe({trait_def_id:?})")
}
ty::PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => {
write!(f, "ClosureKind({closure_def_id:?}, {closure_args:?}, {kind:?})")
}
ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({c1:?}, {c2:?})"),
ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"),
ty::PredicateKind::AliasRelate(t1, t2, dir) => {
write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})")
}
}
}
}
impl<'tcx> fmt::Debug for AliasTy<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
WithInfcx::with_no_infcx(self).fmt(f)
@ -486,7 +449,6 @@ TrivialTypeTraversalImpls! {
crate::ty::IntVarValue,
crate::ty::adjustment::PointerCoercion,
crate::ty::RegionVid,
crate::ty::UniverseIndex,
crate::ty::Variance,
::rustc_span::Span,
::rustc_span::symbol::Ident,
@ -503,7 +465,6 @@ TrivialTypeTraversalAndLiftImpls! {
::rustc_hir::Mutability,
::rustc_hir::Unsafety,
::rustc_target::spec::abi::Abi,
crate::ty::AliasRelationDirection,
crate::ty::ClosureKind,
crate::ty::ParamConst,
crate::ty::ParamTy,

View File

@ -32,10 +32,12 @@ use std::fmt;
use std::ops::{ControlFlow, Deref, Range};
use ty::util::IntTypeExt;
use rustc_type_ir::ClauseKind as IrClauseKind;
use rustc_type_ir::CollectAndApply;
use rustc_type_ir::ConstKind as IrConstKind;
use rustc_type_ir::DebugWithInfcx;
use rustc_type_ir::DynKind;
use rustc_type_ir::PredicateKind as IrPredicateKind;
use rustc_type_ir::RegionKind as IrRegionKind;
use rustc_type_ir::TyKind as IrTyKind;
use rustc_type_ir::TyKind::*;
@ -47,6 +49,8 @@ use super::GenericParamDefKind;
pub type TyKind<'tcx> = IrTyKind<TyCtxt<'tcx>>;
pub type RegionKind<'tcx> = IrRegionKind<TyCtxt<'tcx>>;
pub type ConstKind<'tcx> = IrConstKind<TyCtxt<'tcx>>;
pub type PredicateKind<'tcx> = IrPredicateKind<TyCtxt<'tcx>>;
pub type ClauseKind<'tcx> = IrClauseKind<TyCtxt<'tcx>>;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]

View File

@ -594,10 +594,27 @@ pub struct CanonicalUserTypeAnnotation<'tcx> {
/// Canonical user type annotation.
pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>;
impl<'tcx> CanonicalUserType<'tcx> {
/// A user-given type annotation attached to a constant. These arise
/// from constants that are named via paths, like `Foo::<A>::new` and
/// so forth.
#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)]
#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum UserType<'tcx> {
Ty(Ty<'tcx>),
/// The canonical type is the result of `type_of(def_id)` with the
/// given substitutions applied.
TypeOf(DefId, UserArgs<'tcx>),
}
pub trait IsIdentity {
fn is_identity(&self) -> bool;
}
impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
/// Returns `true` if this represents a substitution of the form `[?0, ?1, ?2]`,
/// i.e., each thing is mapped to a canonical variable with the same index.
pub fn is_identity(&self) -> bool {
fn is_identity(&self) -> bool {
match self.value {
UserType::Ty(_) => false,
UserType::TypeOf(_, user_args) => {
@ -640,19 +657,6 @@ impl<'tcx> CanonicalUserType<'tcx> {
}
}
/// A user-given type annotation attached to a constant. These arise
/// from constants that are named via paths, like `Foo::<A>::new` and
/// so forth.
#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)]
#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum UserType<'tcx> {
Ty(Ty<'tcx>),
/// The canonical type is the result of `type_of(def_id)` with the
/// given substitutions applied.
TypeOf(DefId, UserArgs<'tcx>),
}
impl<'tcx> std::fmt::Display for UserType<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {

View File

@ -48,7 +48,7 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
}
fn has_coroutines(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_TY_GENERATOR)
self.has_type_flags(TypeFlags::HAS_TY_COROUTINE)
}
fn references_error(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_ERROR)

View File

@ -379,6 +379,5 @@ mir_build_unused_unsafe = unnecessary `unsafe` block
.label = unnecessary `unsafe` block
mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block
mir_build_unused_unsafe_enclosing_fn_label = because it's nested under this `unsafe` fn
mir_build_variant_defined_here = not covered

View File

@ -157,7 +157,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// [ 0. Pre-match ]
/// |
/// [ 1. Evaluate Scrutinee (expression being matched on) ]
/// [ (fake read of scrutinee) ]
/// [ (PlaceMention of scrutinee) ]
/// |
/// [ 2. Decision tree -- check discriminants ] <--------+
/// | |
@ -184,7 +184,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
///
/// We generate MIR in the following steps:
///
/// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]).
/// 1. Evaluate the scrutinee and add the PlaceMention of it ([Builder::lower_scrutinee]).
/// 2. Create the decision tree ([Builder::lower_match_tree]).
/// 3. Determine the fake borrows that are needed from the places that were
/// matched against and create the required temporaries for them
@ -223,6 +223,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_temps = self.lower_match_tree(
block,
scrutinee_span,
&scrutinee_place,
match_start_span,
match_has_guard,
&mut candidates,
@ -238,7 +239,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
)
}
/// Evaluate the scrutinee and add the fake read of it.
/// Evaluate the scrutinee and add the PlaceMention for it.
fn lower_scrutinee(
&mut self,
mut block: BasicBlock,
@ -246,26 +247,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
scrutinee_span: Span,
) -> BlockAnd<PlaceBuilder<'tcx>> {
let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee));
// Matching on a `scrutinee_place` with an uninhabited type doesn't
// generate any memory reads by itself, and so if the place "expression"
// contains unsafe operations like raw pointer dereferences or union
// field projections, we wouldn't know to require an `unsafe` block
// around a `match` equivalent to `std::intrinsics::unreachable()`.
// See issue #47412 for this hole being discovered in the wild.
//
// HACK(eddyb) Work around the above issue by adding a dummy inspection
// of `scrutinee_place`, specifically by applying `ReadForMatch`.
//
// NOTE: ReadForMatch also checks that the scrutinee is initialized.
// This is currently needed to not allow matching on an uninitialized,
// uninhabited value. If we get never patterns, those will check that
// the place is initialized, and so this read would only be used to
// check safety.
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
let source_info = self.source_info(scrutinee_span);
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
let source_info = self.source_info(scrutinee_span);
self.cfg.push_place_mention(block, source_info, scrutinee_place);
}
block.and(scrutinee_place_builder)
@ -304,6 +288,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
block: BasicBlock,
scrutinee_span: Span,
scrutinee_place_builder: &PlaceBuilder<'tcx>,
match_start_span: Span,
match_has_guard: bool,
candidates: &mut [&mut Candidate<'pat, 'tcx>],
@ -331,6 +316,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// otherwise block. Match checking will ensure this is actually
// unreachable.
let source_info = self.source_info(scrutinee_span);
// Matching on a `scrutinee_place` with an uninhabited type doesn't
// generate any memory reads by itself, and so if the place "expression"
// contains unsafe operations like raw pointer dereferences or union
// field projections, we wouldn't know to require an `unsafe` block
// around a `match` equivalent to `std::intrinsics::unreachable()`.
// See issue #47412 for this hole being discovered in the wild.
//
// HACK(eddyb) Work around the above issue by adding a dummy inspection
// of `scrutinee_place`, specifically by applying `ReadForMatch`.
//
// NOTE: ReadForMatch also checks that the scrutinee is initialized.
// This is currently needed to not allow matching on an uninitialized,
// uninhabited value. If we get never patterns, those will check that
// the place is initialized, and so this read would only be used to
// check safety.
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
self.cfg.push_fake_read(
otherwise_block,
source_info,
cause_matched_place,
scrutinee_place,
);
}
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
}
@ -599,13 +611,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
_ => {
let place_builder = unpack!(block = self.as_place_builder(block, initializer));
if let Some(place) = place_builder.try_to_place(self) {
let source_info = self.source_info(initializer.span);
self.cfg.push_place_mention(block, source_info, place);
}
let place_builder =
unpack!(block = self.lower_scrutinee(block, initializer, initializer.span));
self.place_into_pattern(block, &irrefutable_pat, place_builder, true)
}
}
@ -622,6 +629,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_temps = self.lower_match_tree(
block,
irrefutable_pat.span,
&initializer,
irrefutable_pat.span,
false,
&mut [&mut candidate],
@ -1841,6 +1849,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_temps = self.lower_match_tree(
block,
pat.span,
&expr_place_builder,
pat.span,
false,
&mut [&mut guard_candidate, &mut otherwise_candidate],
@ -2342,6 +2351,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fake_borrow_temps = this.lower_match_tree(
block,
initializer_span,
&scrutinee,
pattern.span,
false,
&mut [&mut candidate, &mut wildcard],

View File

@ -13,6 +13,7 @@ use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
use std::mem;
use std::ops::Bound;
struct UnsafetyVisitor<'a, 'tcx> {
@ -24,7 +25,6 @@ struct UnsafetyVisitor<'a, 'tcx> {
/// The current "safety context". This notably tracks whether we are in an
/// `unsafe` block, and whether it has been used.
safety_context: SafetyContext,
body_unsafety: BodyUnsafety,
/// The `#[target_feature]` attributes of the body. Used for checking
/// calls to functions with `#[target_feature]` (RFC 2396).
body_target_features: &'tcx [Symbol],
@ -34,43 +34,50 @@ struct UnsafetyVisitor<'a, 'tcx> {
in_union_destructure: bool,
param_env: ParamEnv<'tcx>,
inside_adt: bool,
warnings: &'a mut Vec<UnusedUnsafeWarning>,
}
impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
if let (
SafetyContext::UnsafeBlock { span: enclosing_span, .. },
SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
) = (self.safety_context, safety_context)
let prev_context = mem::replace(&mut self.safety_context, safety_context);
f(self);
let safety_context = mem::replace(&mut self.safety_context, prev_context);
if let SafetyContext::UnsafeBlock { used, span, hir_id, nested_used_blocks } =
safety_context
{
self.warn_unused_unsafe(
hir_id,
block_span,
Some(UnusedUnsafeEnclosing::Block {
span: self.tcx.sess.source_map().guess_head_span(enclosing_span),
}),
);
f(self);
} else {
let prev_context = self.safety_context;
self.safety_context = safety_context;
if !used {
self.warn_unused_unsafe(hir_id, span, None);
f(self);
if let SafetyContext::UnsafeBlock {
nested_used_blocks: ref mut prev_nested_used_blocks,
..
} = self.safety_context
{
prev_nested_used_blocks.extend(nested_used_blocks);
}
} else {
for block in nested_used_blocks {
self.warn_unused_unsafe(
block.hir_id,
block.span,
Some(UnusedUnsafeEnclosing::Block {
span: self.tcx.sess.source_map().guess_head_span(span),
}),
);
}
if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
self.warn_unused_unsafe(
hir_id,
span,
if self.unsafe_op_in_unsafe_fn_allowed() {
self.body_unsafety
.unsafe_fn_sig_span()
.map(|span| UnusedUnsafeEnclosing::Function { span })
} else {
None
},
);
match self.safety_context {
SafetyContext::UnsafeBlock {
nested_used_blocks: ref mut prev_nested_used_blocks,
..
} => {
prev_nested_used_blocks.push(NestedUsedBlock { hir_id, span });
}
_ => (),
}
}
self.safety_context = prev_context;
}
}
@ -102,18 +109,12 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
}
fn warn_unused_unsafe(
&self,
&mut self,
hir_id: hir::HirId,
block_span: Span,
enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
) {
let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
self.tcx.emit_spanned_lint(
UNUSED_UNSAFE,
hir_id,
block_span,
UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
);
self.warnings.push(UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe });
}
/// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
@ -128,7 +129,14 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
self.tcx.ensure_with_value().mir_built(def);
let inner_thir = &inner_thir.steal();
let hir_context = self.tcx.hir().local_def_id_to_hir_id(def);
let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self };
let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe);
let mut inner_visitor = UnsafetyVisitor {
thir: inner_thir,
hir_context,
safety_context,
warnings: self.warnings,
..*self
};
inner_visitor.visit_expr(&inner_thir[expr]);
// Unsafe blocks can be used in the inner body, make sure to take it into account
self.safety_context = inner_visitor.safety_context;
@ -195,8 +203,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
});
}
BlockSafety::ExplicitUnsafe(hir_id) => {
let used =
matches!(self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id), (Level::Allow, _));
self.in_safety_context(
SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
SafetyContext::UnsafeBlock {
span: block.span,
hir_id,
used,
nested_used_blocks: Vec::new(),
},
|this| visit::walk_block(this, block),
);
}
@ -481,36 +496,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
}
}
#[derive(Clone, Copy)]
#[derive(Clone)]
enum SafetyContext {
Safe,
BuiltinUnsafeBlock,
UnsafeFn,
UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
UnsafeBlock {
span: Span,
hir_id: hir::HirId,
used: bool,
nested_used_blocks: Vec<NestedUsedBlock>,
},
}
#[derive(Clone, Copy)]
enum BodyUnsafety {
/// The body is not unsafe.
Safe,
/// The body is an unsafe function. The span points to
/// the signature of the function.
Unsafe(Span),
struct NestedUsedBlock {
hir_id: hir::HirId,
span: Span,
}
impl BodyUnsafety {
/// Returns whether the body is unsafe.
fn is_unsafe(&self) -> bool {
matches!(self, BodyUnsafety::Unsafe(_))
}
/// If the body is unsafe, returns the `Span` of its signature.
fn unsafe_fn_sig_span(self) -> Option<Span> {
match self {
BodyUnsafety::Unsafe(span) => Some(span),
BodyUnsafety::Safe => None,
}
}
struct UnusedUnsafeWarning {
hir_id: hir::HirId,
block_span: Span,
enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
}
#[derive(Clone, Copy, PartialEq)]
@ -803,27 +811,37 @@ pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def);
let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
BodyUnsafety::Unsafe(fn_sig.span)
SafetyContext::UnsafeFn
} else {
BodyUnsafety::Safe
SafetyContext::Safe
}
});
let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
let safety_context =
if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
let mut warnings = Vec::new();
let mut visitor = UnsafetyVisitor {
tcx,
thir,
safety_context,
hir_context: hir_id,
body_unsafety,
body_target_features,
assignment_info: None,
in_union_destructure: false,
param_env: tcx.param_env(def),
inside_adt: false,
warnings: &mut warnings,
};
visitor.visit_expr(&thir[expr]);
warnings.sort_by_key(|w| w.block_span);
for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings {
let block_span = tcx.sess.source_map().guess_head_span(block_span);
tcx.emit_spanned_lint(
UNUSED_UNSAFE,
hir_id,
block_span,
UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
);
}
}

View File

@ -392,11 +392,6 @@ pub enum UnusedUnsafeEnclosing {
#[primary_span]
span: Span,
},
#[label(mir_build_unused_unsafe_enclosing_fn_label)]
Function {
#[primary_span]
span: Span,
},
}
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {

View File

@ -20,7 +20,7 @@ mod build;
mod check_unsafety;
mod errors;
pub mod lints;
pub mod thir;
mod thir;
use rustc_middle::query::Providers;

View File

@ -191,11 +191,16 @@ impl<'tcx> Cx<'tcx> {
source: self.mirror_expr(source),
cast: PointerCoercion::ArrayToPointer,
}
} else {
// check whether this is casting an enum variant discriminant
// to prevent cycles, we refer to the discriminant initializer
} else if let hir::ExprKind::Path(ref qpath) = source.kind
&& let res = self.typeck_results().qpath_res(qpath, source.hir_id)
&& let ty = self.typeck_results().node_type(source.hir_id)
&& let ty::Adt(adt_def, args) = ty.kind()
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res
{
// Check whether this is casting an enum variant discriminant.
// To prevent cycles, we refer to the discriminant initializer,
// which is always an integer and thus doesn't need to know the
// enum's layout (or its tag type) to compute it during const eval
// enum's layout (or its tag type) to compute it during const eval.
// Example:
// enum Foo {
// A,
@ -204,21 +209,6 @@ impl<'tcx> Cx<'tcx> {
// The correct solution would be to add symbolic computations to miri,
// so we wouldn't have to compute and store the actual value
let hir::ExprKind::Path(ref qpath) = source.kind else {
return ExprKind::Cast { source: self.mirror_expr(source) };
};
let res = self.typeck_results().qpath_res(qpath, source.hir_id);
let ty = self.typeck_results().node_type(source.hir_id);
let ty::Adt(adt_def, args) = ty.kind() else {
return ExprKind::Cast { source: self.mirror_expr(source) };
};
let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res
else {
return ExprKind::Cast { source: self.mirror_expr(source) };
};
let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id);
let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx);
@ -255,6 +245,10 @@ impl<'tcx> Cx<'tcx> {
};
ExprKind::Cast { source }
} else {
// Default to `ExprKind::Cast` for all explicit casts.
// MIR building then picks the right MIR casts based on the types.
ExprKind::Cast { source: self.mirror_expr(source) }
}
}

View File

@ -53,14 +53,13 @@ use smallvec::{smallvec, SmallVec};
use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{HirId, RangeEnd};
use rustc_hir::RangeEnd;
use rustc_index::Idx;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::mir;
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
use rustc_session::lint;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
@ -68,7 +67,6 @@ use self::Constructor::*;
use self::SliceKind::*;
use super::usefulness::{MatchCheckCtxt, PatCtxt};
use crate::errors::{Overlap, OverlappingRangeEndpoints};
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
@ -111,15 +109,15 @@ pub(crate) struct IntRange {
impl IntRange {
#[inline]
fn is_integral(ty: Ty<'_>) -> bool {
pub(super) fn is_integral(ty: Ty<'_>) -> bool {
matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool)
}
fn is_singleton(&self) -> bool {
pub(super) fn is_singleton(&self) -> bool {
self.range.start() == self.range.end()
}
fn boundaries(&self) -> (u128, u128) {
pub(super) fn boundaries(&self) -> (u128, u128) {
(*self.range.start(), *self.range.end())
}
@ -177,23 +175,6 @@ impl IntRange {
}
}
fn suspicious_intersection(&self, other: &Self) -> bool {
// `false` in the following cases:
// 1 ---- // 1 ---------- // 1 ---- // 1 ----
// 2 ---------- // 2 ---- // 2 ---- // 2 ----
//
// The following are currently `false`, but could be `true` in the future (#64007):
// 1 --------- // 1 ---------
// 2 ---------- // 2 ----------
//
// `true` in the following cases:
// 1 ------- // 1 -------
// 2 -------- // 2 -------
let (lo, hi) = self.boundaries();
let (other_lo, other_hi) = other.boundaries();
(lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton()
}
/// Partition a range of integers into disjoint subranges. This does constructor splitting for
/// integer ranges as explained at the top of the file.
///
@ -293,7 +274,7 @@ impl IntRange {
}
/// Only used for displaying the range.
fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
pub(super) fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
let (lo, hi) = self.boundaries();
let bias = IntRange::signed_bias(tcx, ty);
@ -315,51 +296,6 @@ impl IntRange {
Pat { ty, span: DUMMY_SP, kind }
}
/// Lint on likely incorrect range patterns (#63987)
pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>(
&self,
pcx: &PatCtxt<'_, 'p, 'tcx>,
pats: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
column_count: usize,
lint_root: HirId,
) {
if self.is_singleton() {
return;
}
if column_count != 1 {
// FIXME: for now, only check for overlapping ranges on simple range
// patterns. Otherwise with the current logic the following is detected
// as overlapping:
// ```
// match (0u8, true) {
// (0 ..= 125, false) => {}
// (125 ..= 255, true) => {}
// _ => {}
// }
// ```
return;
}
let overlap: Vec<_> = pats
.filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span())))
.filter(|(range, _)| self.suspicious_intersection(range))
.map(|(range, span)| Overlap {
range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty),
span,
})
.collect();
if !overlap.is_empty() {
pcx.cx.tcx.emit_spanned_lint(
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
lint_root,
pcx.span,
OverlappingRangeEndpoints { overlap, range: pcx.span },
);
}
}
}
/// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and
@ -644,7 +580,7 @@ impl<'tcx> Constructor<'tcx> {
_ => None,
}
}
fn as_int_range(&self) -> Option<&IntRange> {
pub(super) fn as_int_range(&self) -> Option<&IntRange> {
match self {
IntRange(range) => Some(range),
_ => None,

View File

@ -307,8 +307,10 @@
use self::ArmType::*;
use self::Usefulness::*;
use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, WitnessPat};
use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
use super::deconstruct_pat::{
Constructor, ConstructorSet, DeconstructedPat, IntRange, SplitConstructorSet, WitnessPat,
};
use crate::errors::{NonExhaustiveOmittedPattern, Overlap, OverlappingRangeEndpoints, Uncovered};
use rustc_data_structures::captures::Captures;
@ -317,6 +319,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::def_id::DefId;
use rustc_hir::HirId;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint;
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::{Span, DUMMY_SP};
@ -473,11 +476,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
Matrix { patterns: vec![] }
}
/// Number of columns of this matrix. `None` is the matrix is empty.
pub(super) fn column_count(&self) -> Option<usize> {
self.patterns.get(0).map(|r| r.len())
}
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
/// expands it.
fn push(&mut self, row: PatStack<'p, 'tcx>) {
@ -833,15 +831,6 @@ fn is_useful<'p, 'tcx>(
let v_ctor = v.head().ctor();
debug!(?v_ctor);
if let Constructor::IntRange(ctor_range) = &v_ctor {
// Lint on likely incorrect range patterns (#63987)
ctor_range.lint_overlapping_range_endpoints(
pcx,
matrix.heads(),
matrix.column_count().unwrap_or(0),
lint_root,
)
}
// We split the head constructor of `v`.
let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
// For each constructor, we compute whether there's a value that starts with it that would
@ -875,22 +864,102 @@ fn is_useful<'p, 'tcx>(
ret
}
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
/// inspect the same subvalue".
/// This is used to traverse patterns column-by-column for lints. Despite similarities with
/// `is_useful`, this is a different traversal. Notably this is linear in the depth of patterns,
/// whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
#[derive(Debug)]
struct PatternColumn<'p, 'tcx> {
patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>,
}
impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
fn new(patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>) -> Self {
Self { patterns }
}
fn is_empty(&self) -> bool {
self.patterns.is_empty()
}
fn head_ty(&self) -> Option<Ty<'tcx>> {
if self.patterns.len() == 0 {
return None;
}
// If the type is opaque and it is revealed anywhere in the column, we take the revealed
// version. Otherwise we could encounter constructors for the revealed type and crash.
let is_opaque = |ty: Ty<'tcx>| matches!(ty.kind(), ty::Alias(ty::Opaque, ..));
let first_ty = self.patterns[0].ty();
if is_opaque(first_ty) {
for pat in &self.patterns {
let ty = pat.ty();
if !is_opaque(ty) {
return Some(ty);
}
}
}
Some(first_ty)
}
fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> {
let column_ctors = self.patterns.iter().map(|p| p.ctor());
ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column_ctors)
}
fn iter<'a>(&'a self) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
self.patterns.iter().copied()
}
/// Does specialization: given a constructor, this takes the patterns from the column that match
/// the constructor, and outputs their fields.
/// This returns one column per field of the constructor. The normally all have the same length
/// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns
/// which may change the lengths.
fn specialize(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Vec<Self> {
let arity = ctor.arity(pcx);
if arity == 0 {
return Vec::new();
}
// We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
// columns may have different lengths in the presence of or-patterns (this is why we can't
// reuse `Matrix`).
let mut specialized_columns: Vec<_> =
(0..arity).map(|_| Self { patterns: Vec::new() }).collect();
let relevant_patterns =
self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
for pat in relevant_patterns {
let specialized = pat.specialize(pcx, &ctor);
for (subpat, column) in specialized.iter().zip(&mut specialized_columns) {
if subpat.is_or_pat() {
column.patterns.extend(subpat.iter_fields())
} else {
column.patterns.push(subpat)
}
}
}
assert!(
!specialized_columns[0].is_empty(),
"ctor {ctor:?} was listed as present but isn't;
there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`"
);
specialized_columns
}
}
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
/// in a given column. This traverses patterns column-by-column, where a column is the intuitive
/// notion of "subpatterns that inspect the same subvalue".
/// Despite similarities with `is_useful`, this traversal is different. Notably this is linear in the
/// depth of patterns, whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
/// in a given column.
#[instrument(level = "debug", skip(cx), ret)]
fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
column: &[&DeconstructedPat<'p, 'tcx>],
column: &PatternColumn<'p, 'tcx>,
) -> Vec<WitnessPat<'tcx>> {
if column.is_empty() {
let Some(ty) = column.head_ty() else {
return Vec::new();
}
let ty = column[0].ty();
};
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };
let set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column.iter().map(|p| p.ctor()));
let set = column.analyze_ctors(pcx);
if set.present.is_empty() {
// We can't consistently handle the case where no constructors are present (since this would
// require digging deep through any type in case there's a non_exhaustive enum somewhere),
@ -911,35 +980,11 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
// Recurse into the fields.
for ctor in set.present {
let arity = ctor.arity(pcx);
if arity == 0 {
continue;
}
// We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
// columns may have different lengths in the presence of or-patterns (this is why we can't
// reuse `Matrix`).
let mut specialized_columns: Vec<Vec<_>> = (0..arity).map(|_| Vec::new()).collect();
let relevant_patterns = column.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
for pat in relevant_patterns {
let specialized = pat.specialize(pcx, &ctor);
for (subpat, sub_column) in specialized.iter().zip(&mut specialized_columns) {
if subpat.is_or_pat() {
sub_column.extend(subpat.iter_fields())
} else {
sub_column.push(subpat)
}
}
}
debug_assert!(
!specialized_columns[0].is_empty(),
"ctor {ctor:?} was listed as present but isn't"
);
let specialized_columns = column.specialize(pcx, &ctor);
let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
for (i, col_i) in specialized_columns.iter().enumerate() {
// Compute witnesses for each column.
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i.as_slice());
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i);
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
// adding enough wildcards to match `arity`.
for wit in wits_for_col_i {
@ -952,6 +997,81 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
witnesses
}
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
#[instrument(level = "debug", skip(cx, lint_root))]
fn lint_overlapping_range_endpoints<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
column: &PatternColumn<'p, 'tcx>,
lint_root: HirId,
) {
let Some(ty) = column.head_ty() else {
return;
};
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };
let set = column.analyze_ctors(pcx);
if IntRange::is_integral(ty) {
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
let overlap_as_pat = overlap.to_pat(cx.tcx, ty);
let overlaps: Vec<_> = overlapped_spans
.iter()
.copied()
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
.collect();
cx.tcx.emit_spanned_lint(
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
lint_root,
this_span,
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
);
};
// If two ranges overlapped, the split set will contain their intersection as a singleton.
let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range());
for overlap_range in split_int_ranges.clone() {
if overlap_range.is_singleton() {
let overlap: u128 = overlap_range.boundaries().0;
// Spans of ranges that start or end with the overlap.
let mut prefixes: SmallVec<[_; 1]> = Default::default();
let mut suffixes: SmallVec<[_; 1]> = Default::default();
// Iterate on patterns that contained `overlap`.
for pat in column.iter() {
let this_span = pat.span();
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
if this_range.is_singleton() {
// Don't lint when one of the ranges is a singleton.
continue;
}
let (start, end) = this_range.boundaries();
if start == overlap {
// `this_range` looks like `overlap..=end`; it overlaps with any ranges that
// look like `start..=overlap`.
if !prefixes.is_empty() {
emit_lint(overlap_range, this_span, &prefixes);
}
suffixes.push(this_span)
} else if end == overlap {
// `this_range` looks like `start..=overlap`; it overlaps with any ranges
// that look like `overlap..=end`.
if !suffixes.is_empty() {
emit_lint(overlap_range, this_span, &suffixes);
}
prefixes.push(this_span)
}
}
}
}
} else {
// Recurse into the fields.
for ctor in set.present {
for col in column.specialize(pcx, &ctor) {
lint_overlapping_range_endpoints(cx, &col, lint_root);
}
}
}
}
/// The arm of a match expression.
#[derive(Clone, Copy, Debug)]
pub(crate) struct MatchArm<'p, 'tcx> {
@ -1022,6 +1142,10 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
NoWitnesses { .. } => bug!(),
};
let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::<Vec<_>>();
let pat_column = PatternColumn::new(pat_column);
lint_overlapping_range_endpoints(cx, &pat_column, lint_root);
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
if cx.refutable
@ -1031,9 +1155,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
rustc_session::lint::Level::Allow
)
{
let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::<Vec<_>>();
let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
if !witnesses.is_empty() {
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
// is not exhaustive enough.

View File

@ -438,10 +438,8 @@ impl<'tcx> Inliner<'tcx> {
return Err("incompatible instruction set");
}
for feature in &callee_attrs.target_features {
if !self.codegen_fn_attrs.target_features.contains(feature) {
return Err("incompatible target feature");
}
if callee_attrs.target_features != self.codegen_fn_attrs.target_features {
return Err("incompatible target features");
}
Ok(())

View File

@ -20,6 +20,7 @@ extern crate tracing;
#[macro_use]
extern crate rustc_middle;
use hir::ConstContext;
use required_consts::RequiredConstsVisitor;
use rustc_const_eval::util;
use rustc_data_structures::fx::FxIndexSet;
@ -251,8 +252,13 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
let const_kind = tcx.hir().body_const_context(def);
// No need to const-check a non-const `fn`.
if const_kind.is_none() {
return Default::default();
match const_kind {
Some(ConstContext::Const { .. } | ConstContext::Static(_))
| Some(ConstContext::ConstFn) => {}
None => span_bug!(
tcx.def_span(def),
"`mir_const_qualif` should only be called on const fns and const items"
),
}
// N.B., this `borrow()` is guaranteed to be valid (i.e., the value
@ -317,7 +323,21 @@ fn mir_promoted(
// Ensure that we compute the `mir_const_qualif` for constants at
// this point, before we steal the mir-const result.
// Also this means promotion can rely on all const checks having been done.
let const_qualifs = tcx.mir_const_qualif(def);
let const_qualifs = match tcx.def_kind(def) {
DefKind::Fn | DefKind::AssocFn | DefKind::Closure
if tcx.constness(def) == hir::Constness::Const
|| tcx.is_const_default_method(def.to_def_id()) =>
{
tcx.mir_const_qualif(def)
}
DefKind::AssocConst
| DefKind::Const
| DefKind::Static(_)
| DefKind::InlineConst
| DefKind::AnonConst => tcx.mir_const_qualif(def),
_ => ConstQualifs::default(),
};
let mut body = tcx.mir_const(def).steal();
if let Some(error_reported) = const_qualifs.tainted_by_errors {
body.tainted_by_errors = Some(error_reported);

View File

@ -504,8 +504,10 @@ impl<'a> Parser<'a> {
// Special-case "expected `;`" errors
if expected.contains(&TokenType::Token(token::Semi)) {
if self.prev_token == token::Question && self.maybe_recover_from_ternary_operator() {
return Ok(true);
// If the user is trying to write a ternary expression, recover it and
// return an Err to prevent a cascade of irrelevant diagnostics
if self.prev_token == token::Question && let Err(e) = self.maybe_recover_from_ternary_operator() {
return Err(e);
}
if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
@ -1428,10 +1430,10 @@ impl<'a> Parser<'a> {
/// Rust has no ternary operator (`cond ? then : else`). Parse it and try
/// to recover from it if `then` and `else` are valid expressions. Returns
/// whether it was a ternary operator.
pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> bool {
/// an err if this appears to be a ternary expression.
pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> {
if self.prev_token != token::Question {
return false;
return PResult::Ok(());
}
let lo = self.prev_token.span.lo();
@ -1449,20 +1451,18 @@ impl<'a> Parser<'a> {
if self.eat_noexpect(&token::Colon) {
match self.parse_expr() {
Ok(_) => {
self.sess.emit_err(TernaryOperator { span: self.token.span.with_lo(lo) });
return true;
return Err(self
.sess
.create_err(TernaryOperator { span: self.token.span.with_lo(lo) }));
}
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
}
};
}
} else {
self.restore_snapshot(snapshot);
};
false
}
self.restore_snapshot(snapshot);
Ok(())
}
pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> {

View File

@ -159,8 +159,9 @@ pub struct Parser<'a> {
/// appropriately.
///
/// See the comments in the `parse_path_segment` function for more details.
unmatched_angle_bracket_count: u32,
max_angle_bracket_count: u32,
unmatched_angle_bracket_count: u16,
max_angle_bracket_count: u16,
angle_bracket_nesting: u16,
last_unexpected_token_span: Option<Span>,
/// If present, this `Parser` is not parsing Rust code but rather a macro call.
@ -394,6 +395,7 @@ impl<'a> Parser<'a> {
break_last_token: false,
unmatched_angle_bracket_count: 0,
max_angle_bracket_count: 0,
angle_bracket_nesting: 0,
last_unexpected_token_span: None,
subparser_name,
capture_state: CaptureState {

View File

@ -487,10 +487,24 @@ impl<'a> Parser<'a> {
// Take a snapshot before attempting to parse - we can restore this later.
let snapshot = is_first_invocation.then(|| self.clone());
self.angle_bracket_nesting += 1;
debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)");
match self.parse_angle_args(ty_generics) {
Ok(args) => Ok(args),
Ok(args) => {
self.angle_bracket_nesting -= 1;
Ok(args)
}
Err(mut e) if self.angle_bracket_nesting > 10 => {
self.angle_bracket_nesting -= 1;
// When encountering severely malformed code where there are several levels of
// nested unclosed angle args (`f::<f::<f::<f::<...`), we avoid severe O(n^2)
// behavior by bailing out earlier (#117080).
e.emit();
rustc_errors::FatalError.raise();
}
Err(e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
self.angle_bracket_nesting -= 1;
// Swap `self` with our backup of the parser state before attempting to parse
// generic arguments.
let snapshot = mem::replace(self, snapshot.unwrap());
@ -520,8 +534,8 @@ impl<'a> Parser<'a> {
// Make a span over ${unmatched angle bracket count} characters.
// This is safe because `all_angle_brackets` ensures that there are only `<`s,
// i.e. no multibyte characters, in this range.
let span =
lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count));
let span = lo
.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count.into()));
self.sess.emit_err(errors::UnmatchedAngle {
span,
plural: snapshot.unmatched_angle_bracket_count > 1,
@ -531,7 +545,10 @@ impl<'a> Parser<'a> {
self.parse_angle_args(ty_generics)
}
}
Err(e) => Err(e),
Err(e) => {
self.angle_bracket_nesting -= 1;
Err(e)
}
}
}

View File

@ -393,6 +393,9 @@ passes_invalid_attr_at_crate_level =
`{$name}` attribute cannot be used at crate level
.suggestion = perhaps you meant to use an outer attribute
passes_invalid_attr_at_crate_level_item =
the inner attribute doesn't annotate this {$kind}
passes_invalid_deprecation_version =
invalid deprecation version found
.label = invalid deprecation version
@ -402,11 +405,6 @@ passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]
passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments
passes_invalid_stability =
invalid stability version found
.label = invalid stability version
.item = the stability attribute annotates this item
passes_lang_item_fn_with_target_feature =
`{$name}` language item function is not allowed to have `#[target_feature]`
.label = `{$name}` language item function is not allowed to have `#[target_feature]`

View File

@ -2534,10 +2534,30 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
if attr.style == AttrStyle::Inner {
for attr_to_check in ATTRS_TO_CHECK {
if attr.has_name(*attr_to_check) {
let item = tcx
.hir()
.items()
.map(|id| tcx.hir().item(id))
.find(|item| !item.span.is_dummy()) // Skip prelude `use`s
.map(|item| errors::ItemFollowingInnerAttr {
span: item.ident.span,
kind: item.kind.descr(),
});
tcx.sess.emit_err(errors::InvalidAttrAtCrateLevel {
span: attr.span,
snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(),
sugg_span: tcx
.sess
.source_map()
.span_to_snippet(attr.span)
.ok()
.filter(|src| src.starts_with("#!["))
.map(|_| {
attr.span
.with_lo(attr.span.lo() + BytePos(1))
.with_hi(attr.span.lo() + BytePos(2))
}),
name: *attr_to_check,
item,
});
}
}

View File

@ -856,8 +856,15 @@ pub struct UnknownLangItem {
pub struct InvalidAttrAtCrateLevel {
pub span: Span,
pub snippet: Option<String>,
pub sugg_span: Option<Span>,
pub name: Symbol,
pub item: Option<ItemFollowingInnerAttr>,
}
#[derive(Clone, Copy)]
pub struct ItemFollowingInnerAttr {
pub span: Span,
pub kind: &'static str,
}
impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
@ -871,15 +878,18 @@ impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
diag.set_arg("name", self.name);
// Only emit an error with a suggestion if we can create a string out
// of the attribute span
if let Some(src) = self.snippet {
let replacement = src.replace("#!", "#");
if let Some(span) = self.sugg_span {
diag.span_suggestion_verbose(
self.span,
span,
fluent::passes_suggestion,
replacement,
String::new(),
rustc_errors::Applicability::MachineApplicable,
);
}
if let Some(item) = self.item {
diag.set_arg("kind", item.kind);
diag.span_label(item.span, fluent::passes_invalid_attr_at_crate_level_item);
}
diag
}
}
@ -1504,16 +1514,6 @@ pub struct UselessStability {
pub item_sp: Span,
}
#[derive(Diagnostic)]
#[diag(passes_invalid_stability)]
pub struct InvalidStability {
#[primary_span]
#[label]
pub span: Span,
#[label(passes_item)]
pub item_sp: Span,
}
#[derive(Diagnostic)]
#[diag(passes_cannot_stabilize_deprecated)]
pub struct CannotStabilizeDeprecated {

View File

@ -5,7 +5,7 @@
//! collect them instead.
use rustc_ast::Attribute;
use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER};
use rustc_attr::VERSION_PLACEHOLDER;
use rustc_hir::intravisit::Visitor;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::lib_features::LibFeatures;
@ -59,7 +59,7 @@ impl<'tcx> LibFeatureCollector<'tcx> {
if let Some(s) = since
&& s.as_str() == VERSION_PLACEHOLDER
{
since = Some(rust_version_symbol());
since = Some(sym::env_CFG_RELEASE);
}
if let Some(feature) = feature {

View File

@ -3,8 +3,8 @@
use crate::errors;
use rustc_attr::{
self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable,
UnstableReason, VERSION_PLACEHOLDER,
self as attr, ConstStability, Since, Stability, StabilityLevel, Unstable, UnstableReason,
VERSION_PLACEHOLDER,
};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_hir as hir;
@ -226,37 +226,43 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) =
(&depr.as_ref().and_then(|(d, _)| d.since), &stab.level)
{
// Explicit version of iter::order::lt to handle parse errors properly
for (dep_v, stab_v) in
iter::zip(dep_since.as_str().split('.'), stab_since.as_str().split('.'))
{
match stab_v.parse::<u64>() {
Err(_) => {
self.tcx.sess.emit_err(errors::InvalidStability { span, item_sp });
break;
}
Ok(stab_vp) => match dep_v.parse::<u64>() {
Ok(dep_vp) => match dep_vp.cmp(&stab_vp) {
Ordering::Less => {
self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated {
span,
item_sp,
});
match stab_since {
Since::Current => {
self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
}
Since::Version(stab_since) => {
// Explicit version of iter::order::lt to handle parse errors properly
for (dep_v, stab_v) in iter::zip(
dep_since.as_str().split('.'),
[stab_since.major, stab_since.minor, stab_since.patch],
) {
match dep_v.parse::<u64>() {
Ok(dep_vp) => match dep_vp.cmp(&u64::from(stab_v)) {
Ordering::Less => {
self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated {
span,
item_sp,
});
break;
}
Ordering::Equal => continue,
Ordering::Greater => break,
},
Err(_) => {
if dep_v != "TBD" {
self.tcx.sess.emit_err(errors::InvalidDeprecationVersion {
span,
item_sp,
});
}
break;
}
Ordering::Equal => continue,
Ordering::Greater => break,
},
Err(_) => {
if dep_v != "TBD" {
self.tcx.sess.emit_err(errors::InvalidDeprecationVersion {
span,
item_sp,
});
}
break;
}
},
}
}
Since::Err => {
// An error already reported. Assume the unparseable stabilization
// version is older than the deprecation version.
}
}
}
@ -1109,7 +1115,7 @@ fn unnecessary_stable_feature_lint(
mut since: Symbol,
) {
if since.as_str() == VERSION_PLACEHOLDER {
since = rust_version_symbol();
since = sym::env_CFG_RELEASE;
}
tcx.emit_spanned_lint(
lint::builtin::STABLE_FEATURES,

View File

@ -210,22 +210,7 @@ where
}
}
}
ty::Alias(ty::Weak, alias) => {
self.def_id_visitor.visit_def_id(alias.def_id, "type alias", &ty);
}
ty::Alias(ty::Projection, proj) => {
if V::SKIP_ASSOC_TYS {
// Visitors searching for minimal visibility/reachability want to
// conservatively approximate associated types like `<Type as Trait>::Alias`
// as visible/reachable even if both `Type` and `Trait` are private.
// Ideally, associated types should be substituted in the same way as
// free type aliases, but this isn't done yet.
return ControlFlow::Continue(());
}
// This will also visit args if necessary, so we don't need to recurse.
return self.visit_projection_ty(proj);
}
ty::Alias(ty::Inherent, data) => {
ty::Alias(kind @ (ty::Inherent | ty::Weak | ty::Projection), data) => {
if V::SKIP_ASSOC_TYS {
// Visitors searching for minimal visibility/reachability want to
// conservatively approximate associated types like `Type::Alias`
@ -235,9 +220,14 @@ where
return ControlFlow::Continue(());
}
let kind = match kind {
ty::Inherent | ty::Projection => "associated type",
ty::Weak => "type alias",
ty::Opaque => unreachable!(),
};
self.def_id_visitor.visit_def_id(
data.def_id,
"associated type",
kind,
&LazyDefPathStr { def_id: data.def_id, tcx },
)?;

View File

@ -197,6 +197,9 @@ macro_rules! handle_cycle_error {
([(fatal_cycle) $($rest:tt)*]) => {{
rustc_query_system::HandleCycleError::Fatal
}};
([(cycle_stash) $($rest:tt)*]) => {{
rustc_query_system::HandleCycleError::Stash
}};
([(cycle_delay_bug) $($rest:tt)*]) => {{
rustc_query_system::HandleCycleError::DelayBug
}};

View File

@ -15,6 +15,7 @@ pub enum HandleCycleError {
Error,
Fatal,
DelayBug,
Stash,
}
#[derive(Subdiagnostic)]

View File

@ -19,7 +19,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::Lock;
#[cfg(parallel_compiler)]
use rustc_data_structures::{outline, sync};
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError};
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError, StashKey};
use rustc_span::{Span, DUMMY_SP};
use std::cell::Cell;
use std::collections::hash_map::Entry;
@ -133,6 +133,17 @@ where
let guar = error.delay_as_bug();
query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
}
Stash => {
let guar = if let Some(root) = cycle_error.cycle.first()
&& let Some(span) = root.query.span
{
error.stash(span, StashKey::Cycle);
qcx.dep_context().sess().delay_span_bug(span, "delayed cycle error")
} else {
error.emit()
};
query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
}
}
}

View File

@ -335,7 +335,7 @@ impl Resolver<'_, '_> {
for unused in visitor.unused_imports.values() {
let mut fixes = Vec::new();
let mut spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) {
let spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) {
UnusedSpanResult::Used => continue,
UnusedSpanResult::FlatUnused(span, remove) => {
fixes.push((remove, String::new()));
@ -353,20 +353,19 @@ impl Resolver<'_, '_> {
}
};
let len = spans.len();
spans.sort();
let ms = MultiSpan::from_spans(spans.clone());
let mut span_snippets = spans
let ms = MultiSpan::from_spans(spans);
let mut span_snippets = ms
.primary_spans()
.iter()
.filter_map(|s| match tcx.sess.source_map().span_to_snippet(*s) {
Ok(s) => Some(format!("`{s}`")),
_ => None,
})
.filter_map(|span| tcx.sess.source_map().span_to_snippet(*span).ok())
.map(|s| format!("`{s}`"))
.collect::<Vec<String>>();
span_snippets.sort();
let msg = format!(
"unused import{}{}",
pluralize!(len),
pluralize!(ms.primary_spans().len()),
if !span_snippets.is_empty() {
format!(": {}", span_snippets.join(", "))
} else {
@ -376,7 +375,7 @@ impl Resolver<'_, '_> {
let fix_msg = if fixes.len() == 1 && fixes[0].0 == unused.item_span {
"remove the whole `use` item"
} else if spans.len() > 1 {
} else if ms.primary_spans().len() > 1 {
"remove the unused imports"
} else {
"remove the unused import"

View File

@ -25,7 +25,7 @@ use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Span, SyntaxContext};
use thin_vec::ThinVec;
use thin_vec::{thin_vec, ThinVec};
use crate::errors::{
AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive,
@ -1147,7 +1147,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
namespace: Namespace,
parent_scope: &ParentScope<'a>,
start_module: Module<'a>,
crate_name: Ident,
crate_path: ThinVec<ast::PathSegment>,
filter_fn: FilterFn,
) -> Vec<ImportSuggestion>
where
@ -1163,8 +1163,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
Some(x) => Some(x),
} {
let in_module_is_extern = !in_module.def_id().is_local();
// We have to visit module children in deterministic order to avoid
// instabilities in reported imports (#43552).
in_module.for_each_child(self, |this, ident, ns, name_binding| {
// avoid non-importable candidates
if !name_binding.is_importable() {
@ -1214,12 +1212,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let res = name_binding.res();
if filter_fn(res) {
// create the path
let mut segms = path_segments.clone();
if lookup_ident.span.at_least_rust_2018() {
let mut segms = if lookup_ident.span.at_least_rust_2018() {
// crate-local absolute paths start with `crate::` in edition 2018
// FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660)
segms.insert(0, ast::PathSegment::from_ident(crate_name));
}
crate_path.clone()
} else {
ThinVec::new()
};
segms.append(&mut path_segments.clone());
segms.push(ast::PathSegment::from_ident(ident));
let path = Path { span: name_binding.span, segments: segms, tokens: None };
@ -1318,18 +1318,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
where
FilterFn: Fn(Res) -> bool,
{
let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))];
let mut suggestions = self.lookup_import_candidates_from_module(
lookup_ident,
namespace,
parent_scope,
self.graph_root,
Ident::with_dummy_span(kw::Crate),
crate_path,
&filter_fn,
);
if lookup_ident.span.at_least_rust_2018() {
let extern_prelude_names = self.extern_prelude.clone();
for (ident, _) in extern_prelude_names.into_iter() {
for ident in self.extern_prelude.clone().into_keys() {
if ident.span.from_expansion() {
// Idents are adjusted to the root context before being
// resolved in the extern prelude, so reporting this to the
@ -1340,13 +1340,43 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
let crate_id = self.crate_loader(|c| c.maybe_process_path_extern(ident.name));
if let Some(crate_id) = crate_id {
let crate_root = self.expect_module(crate_id.as_def_id());
let crate_def_id = crate_id.as_def_id();
let crate_root = self.expect_module(crate_def_id);
// Check if there's already an item in scope with the same name as the crate.
// If so, we have to disambiguate the potential import suggestions by making
// the paths *global* (i.e., by prefixing them with `::`).
let needs_disambiguation =
self.resolutions(parent_scope.module).borrow().iter().any(
|(key, name_resolution)| {
if key.ns == TypeNS
&& key.ident == ident
&& let Some(binding) = name_resolution.borrow().binding
{
match binding.res() {
// No disambiguation needed if the identically named item we
// found in scope actually refers to the crate in question.
Res::Def(_, def_id) => def_id != crate_def_id,
Res::PrimTy(_) => true,
_ => false,
}
} else {
false
}
},
);
let mut crate_path = ThinVec::new();
if needs_disambiguation {
crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP));
}
crate_path.push(ast::PathSegment::from_ident(ident));
suggestions.extend(self.lookup_import_candidates_from_module(
lookup_ident,
namespace,
parent_scope,
crate_root,
ident,
crate_path,
&filter_fn,
));
}
@ -2554,7 +2584,7 @@ fn show_candidates(
candidates.iter().for_each(|c| {
(if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
.push((path_names_to_string(&c.path), c.descr, c.did, &c.note, c.via_import))
.push((pprust::path_to_string(&c.path), c.descr, c.did, &c.note, c.via_import))
});
// we want consistent results across executions, but candidates are produced

View File

@ -1570,7 +1570,26 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
err.set_primary_message(
"cannot initialize a tuple struct which contains private fields",
);
if !def_id.is_local()
&& self
.r
.tcx
.inherent_impls(def_id)
.iter()
.flat_map(|impl_def_id| {
self.r.tcx.provided_trait_methods(*impl_def_id)
})
.any(|assoc| !assoc.fn_has_self_parameter && assoc.name == sym::new)
{
// FIXME: look for associated functions with Self return type,
// instead of relying only on the name and lack of self receiver.
err.span_suggestion_verbose(
span.shrink_to_hi(),
"you might have meant to use the `new` associated function",
"::new".to_string(),
Applicability::MaybeIncorrect,
);
}
// Use spans of the tuple struct definition.
self.r.field_def_ids(def_id).map(|field_ids| {
field_ids

View File

@ -2739,32 +2739,25 @@ pub fn build_session_options(
_ => {}
}
// Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes
// precedence.
match (cg.instrument_coverage, unstable_opts.instrument_coverage) {
(Some(ic_c), Some(ic_z)) if ic_c != ic_z => {
handler.early_error(
"incompatible values passed for `-C instrument-coverage` \
and `-Z instrument-coverage`",
);
// Check for unstable values of `-C instrument-coverage`.
// This is what prevents them from being used on stable compilers.
match cg.instrument_coverage {
// Stable values:
InstrumentCoverage::All | InstrumentCoverage::Off => {}
// Unstable values:
InstrumentCoverage::Branch
| InstrumentCoverage::ExceptUnusedFunctions
| InstrumentCoverage::ExceptUnusedGenerics => {
if !unstable_opts.unstable_options {
handler.early_error(
"`-C instrument-coverage=branch` and `-C instrument-coverage=except-*` \
require `-Z unstable-options`",
);
}
}
(Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {}
(Some(_), _) if !unstable_opts.unstable_options => {
handler.early_error(
"`-C instrument-coverage=branch` and `-C instrument-coverage=except-*` \
require `-Z unstable-options`",
);
}
(None, None) => {}
(None, ic) => {
handler
.early_warn("`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`");
cg.instrument_coverage = ic;
}
_ => {}
}
if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) {
if cg.instrument_coverage != InstrumentCoverage::Off {
if cg.profile_generate.enabled() || cg.profile_use.is_some() {
handler.early_error(
"option `-C instrument-coverage` is not compatible with either `-C profile-use` \

View File

@ -294,7 +294,7 @@ impl CodegenOptions {
// JUSTIFICATION: defn of the suggested wrapper fn
#[allow(rustc::bad_opt_access)]
pub fn instrument_coverage(&self) -> InstrumentCoverage {
self.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
self.instrument_coverage
}
}
@ -913,23 +913,23 @@ mod parse {
}
pub(crate) fn parse_instrument_coverage(
slot: &mut Option<InstrumentCoverage>,
slot: &mut InstrumentCoverage,
v: Option<&str>,
) -> bool {
if v.is_some() {
let mut bool_arg = None;
if parse_opt_bool(&mut bool_arg, v) {
*slot = bool_arg.unwrap().then_some(InstrumentCoverage::All);
let mut bool_arg = false;
if parse_bool(&mut bool_arg, v) {
*slot = if bool_arg { InstrumentCoverage::All } else { InstrumentCoverage::Off };
return true;
}
}
let Some(v) = v else {
*slot = Some(InstrumentCoverage::All);
*slot = InstrumentCoverage::All;
return true;
};
*slot = Some(match v {
*slot = match v {
"all" => InstrumentCoverage::All,
"branch" => InstrumentCoverage::Branch,
"except-unused-generics" | "except_unused_generics" => {
@ -940,7 +940,7 @@ mod parse {
}
"off" | "no" | "n" | "false" | "0" => InstrumentCoverage::Off,
_ => return false,
});
};
true
}
@ -1352,7 +1352,7 @@ options! {
inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED],
"set the threshold for inlining a function"),
#[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")]
instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
instrument_coverage: InstrumentCoverage = (InstrumentCoverage::Off, parse_instrument_coverage, [TRACKED],
"instrument the generated code to support LLVM source-based code coverage \
reports (note, the compiler build config must include `profiler = true`); \
implies `-C symbol-mangling-version=v0`. Optional values are:
@ -1593,16 +1593,6 @@ options! {
"a default MIR inlining threshold (default: 50)"),
input_stats: bool = (false, parse_bool, [UNTRACKED],
"gather statistics about the input (default: no)"),
#[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")]
instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
"instrument the generated code to support LLVM source-based code coverage \
reports (note, the compiler build config must include `profiler = true`); \
implies `-C symbol-mangling-version=v0`. Optional values are:
`=all` (implicit value)
`=branch`
`=except-unused-generics`
`=except-unused-functions`
`=off` (default)"),
instrument_mcount: bool = (false, parse_bool, [TRACKED],
"insert function instrument code for mcount-based tracing (default: no)"),
instrument_xray: Option<InstrumentXRay> = (None, parse_instrument_xray, [TRACKED],

View File

@ -45,7 +45,7 @@ use std::fmt;
use std::ops::{Div, Mul};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
use std::sync::{atomic::AtomicBool, Arc};
use std::time::Duration;
pub struct OptimizationFuel {
@ -202,6 +202,12 @@ pub struct Session {
/// The version of the rustc process, possibly including a commit hash and description.
pub cfg_version: &'static str,
/// The inner atomic value is set to true when a feature marked as `internal` is
/// enabled. Makes it so that "please report a bug" is hidden, as ICEs with
/// internal features are wontfix, and they are usually the cause of the ICEs.
/// None signifies that this is not tracked.
pub using_internal_features: Arc<AtomicBool>,
/// All commandline args used to invoke the compiler, with @file args fully expanded.
/// This will only be used within debug info, e.g. in the pdb file on windows
/// This is mainly useful for other tools that reads that debuginfo to figure out
@ -1389,6 +1395,7 @@ pub fn build_session(
target_override: Option<Target>,
cfg_version: &'static str,
ice_file: Option<PathBuf>,
using_internal_features: Arc<AtomicBool>,
expanded_args: Vec<String>,
) -> Session {
// FIXME: This is not general enough to make the warning lint completely override
@ -1525,6 +1532,7 @@ pub fn build_session(
target_features: Default::default(),
unstable_target_features: Default::default(),
cfg_version,
using_internal_features,
expanded_args,
};

View File

@ -13,6 +13,7 @@
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(rustc::usage_of_ty_tykind)]
pub mod rustc_internal;

View File

@ -4,7 +4,7 @@
//! due to incomplete stable coverage.
// Prefer importing stable_mir over internal rustc constructs to make this file more readable.
use crate::rustc_smir::{MaybeStable, Tables};
use crate::rustc_smir::Tables;
use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy};
use stable_mir::ty::{Const, GenericArgKind, GenericArgs, Region, Ty};
use stable_mir::DefId;
@ -31,7 +31,7 @@ impl<'tcx> RustcInternal<'tcx> for GenericArgKind {
match self {
GenericArgKind::Lifetime(reg) => reg.internal(tables).into(),
GenericArgKind::Type(ty) => ty.internal(tables).into(),
GenericArgKind::Const(cnst) => cnst.internal(tables).into(),
GenericArgKind::Const(cnst) => ty_const(cnst, tables).into(),
}
}
}
@ -46,16 +46,22 @@ impl<'tcx> RustcInternal<'tcx> for Region {
impl<'tcx> RustcInternal<'tcx> for Ty {
type T = InternalTy<'tcx>;
fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T {
match tables.types[self.0] {
MaybeStable::Stable(_) => todo!(),
MaybeStable::Rustc(ty) => ty,
tables.types[*self]
}
}
fn ty_const<'tcx>(constant: &Const, tables: &mut Tables<'tcx>) -> rustc_ty::Const<'tcx> {
match constant.internal(tables) {
rustc_middle::mir::Const::Ty(c) => c,
cnst => {
panic!("Trying to covert constant `{constant:?}` to type constant, but found {cnst:?}")
}
}
}
impl<'tcx> RustcInternal<'tcx> for Const {
type T = rustc_ty::Const<'tcx>;
fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T {
todo!()
type T = rustc_middle::mir::Const<'tcx>;
fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T {
tables.constants[self.id]
}
}

View File

@ -167,8 +167,9 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) {
def_ids: IndexMap::default(),
alloc_ids: IndexMap::default(),
spans: IndexMap::default(),
types: vec![],
types: IndexMap::default(),
instances: IndexMap::default(),
constants: IndexMap::default(),
}));
stable_mir::run(&tables, || init(&tables, f));
}

View File

@ -20,7 +20,8 @@ use rustc_target::abi::FieldIdx;
use stable_mir::mir::mono::InstanceDef;
use stable_mir::mir::{Body, CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx};
use stable_mir::ty::{
FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, Span, TyKind, UintTy,
Const, ConstId, ConstantKind, FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy,
Span, TyKind, UintTy,
};
use stable_mir::{self, opaque, Context, Filename};
use std::cell::RefCell;
@ -147,14 +148,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind {
let mut tables = self.0.borrow_mut();
tables.types[ty.0].clone().stable(&mut *tables)
}
fn mk_ty(&self, kind: TyKind) -> stable_mir::ty::Ty {
let mut tables = self.0.borrow_mut();
let n = tables.types.len();
tables.types.push(MaybeStable::Stable(kind));
stable_mir::ty::Ty(n)
tables.types[ty].kind().stable(&mut *tables)
}
fn generics_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::Generics {
@ -213,8 +207,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty {
let mut tables = self.0.borrow_mut();
let instance = tables.instances[def];
let ty = instance.ty(tables.tcx, ParamEnv::empty());
tables.intern_ty(ty)
instance.ty(tables.tcx, ParamEnv::empty()).stable(&mut *tables)
}
fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId {
@ -252,33 +245,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
}
}
#[derive(Clone)]
pub enum MaybeStable<S, R> {
Stable(S),
Rustc(R),
}
impl<'tcx, S, R> MaybeStable<S, R> {
fn stable(self, tables: &mut Tables<'tcx>) -> S
where
R: Stable<'tcx, T = S>,
{
match self {
MaybeStable::Stable(s) => s,
MaybeStable::Rustc(r) => r.stable(tables),
}
}
}
impl<S, R: PartialEq> PartialEq<R> for MaybeStable<S, R> {
fn eq(&self, other: &R) -> bool {
match self {
MaybeStable::Stable(_) => false,
MaybeStable::Rustc(r) => r == other,
}
}
}
pub(crate) struct TablesWrapper<'tcx>(pub(crate) RefCell<Tables<'tcx>>);
pub struct Tables<'tcx> {
@ -286,18 +252,18 @@ pub struct Tables<'tcx> {
pub(crate) def_ids: IndexMap<DefId, stable_mir::DefId>,
pub(crate) alloc_ids: IndexMap<AllocId, stable_mir::AllocId>,
pub(crate) spans: IndexMap<rustc_span::Span, Span>,
pub(crate) types: Vec<MaybeStable<TyKind, Ty<'tcx>>>,
pub(crate) types: IndexMap<Ty<'tcx>, stable_mir::ty::Ty>,
pub(crate) instances: IndexMap<ty::Instance<'tcx>, InstanceDef>,
pub(crate) constants: IndexMap<mir::Const<'tcx>, ConstId>,
}
impl<'tcx> Tables<'tcx> {
fn intern_ty(&mut self, ty: Ty<'tcx>) -> stable_mir::ty::Ty {
if let Some(id) = self.types.iter().position(|t| *t == ty) {
return stable_mir::ty::Ty(id);
}
let id = self.types.len();
self.types.push(MaybeStable::Rustc(ty));
stable_mir::ty::Ty(id)
self.types.create_or_fetch(ty)
}
fn intern_const(&mut self, constant: mir::Const<'tcx>) -> ConstId {
self.constants.create_or_fetch(constant)
}
}
@ -321,9 +287,8 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
type T = stable_mir::mir::Body;
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
stable_mir::mir::Body {
blocks: self
.basic_blocks
stable_mir::mir::Body::new(
self.basic_blocks
.iter()
.map(|block| stable_mir::mir::BasicBlock {
terminator: block.terminator().stable(tables),
@ -334,15 +299,15 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
.collect(),
})
.collect(),
locals: self
.local_decls
self.local_decls
.iter()
.map(|decl| stable_mir::mir::LocalDecl {
ty: tables.intern_ty(decl.ty),
ty: decl.ty.stable(tables),
span: decl.source_info.span.stable(tables),
})
.collect(),
}
self.arg_count,
)
}
}
@ -436,7 +401,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
Cast(cast_kind, op, ty) => stable_mir::mir::Rvalue::Cast(
cast_kind.stable(tables),
op.stable(tables),
tables.intern_ty(*ty),
ty.stable(tables),
),
BinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::BinaryOp(
bin_op.stable(tables),
@ -449,7 +414,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
ops.1.stable(tables),
),
NullaryOp(null_op, ty) => {
stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), tables.intern_ty(*ty))
stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), ty.stable(tables))
}
UnaryOp(un_op, op) => {
stable_mir::mir::Rvalue::UnaryOp(un_op.stable(tables), op.stable(tables))
@ -460,7 +425,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
stable_mir::mir::Rvalue::Aggregate(agg_kind.stable(tables), operands)
}
ShallowInitBox(op, ty) => {
stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), tables.intern_ty(*ty))
stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), ty.stable(tables))
}
CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)),
}
@ -604,7 +569,7 @@ impl<'tcx> Stable<'tcx> for ty::TermKind<'tcx> {
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
use stable_mir::ty::TermKind;
match self {
ty::TermKind::Ty(ty) => TermKind::Type(tables.intern_ty(*ty)),
ty::TermKind::Ty(ty) => TermKind::Type(ty.stable(tables)),
ty::TermKind::Const(cnst) => {
let cnst = cnst.stable(tables);
TermKind::Const(cnst)
@ -885,7 +850,7 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> {
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
match self {
mir::AggregateKind::Array(ty) => {
stable_mir::mir::AggregateKind::Array(tables.intern_ty(*ty))
stable_mir::mir::AggregateKind::Array(ty.stable(tables))
}
mir::AggregateKind::Tuple => stable_mir::mir::AggregateKind::Tuple,
mir::AggregateKind::Adt(def_id, var_idx, generic_arg, user_ty_index, field_idx) => {
@ -917,13 +882,13 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> {
impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
type T = stable_mir::mir::CoroutineKind;
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
use rustc_hir::{AsyncCoroutineKind, CoroutineKind};
use rustc_hir::{CoroutineKind, CoroutineSource};
match self {
CoroutineKind::Async(async_gen) => {
let async_gen = match async_gen {
AsyncCoroutineKind::Block => stable_mir::mir::AsyncCoroutineKind::Block,
AsyncCoroutineKind::Closure => stable_mir::mir::AsyncCoroutineKind::Closure,
AsyncCoroutineKind::Fn => stable_mir::mir::AsyncCoroutineKind::Fn,
CoroutineSource::Block => stable_mir::mir::CoroutineSource::Block,
CoroutineSource::Closure => stable_mir::mir::CoroutineSource::Closure,
CoroutineSource::Fn => stable_mir::mir::CoroutineSource::Fn,
};
stable_mir::mir::CoroutineKind::Async(async_gen)
}
@ -1053,7 +1018,7 @@ impl<'tcx> Stable<'tcx> for ty::GenericArgKind<'tcx> {
use stable_mir::ty::GenericArgKind;
match self {
ty::GenericArgKind::Lifetime(region) => GenericArgKind::Lifetime(region.stable(tables)),
ty::GenericArgKind::Type(ty) => GenericArgKind::Type(tables.intern_ty(*ty)),
ty::GenericArgKind::Type(ty) => GenericArgKind::Type(ty.stable(tables)),
ty::GenericArgKind::Const(cnst) => GenericArgKind::Const(cnst.stable(tables)),
}
}
@ -1099,11 +1064,7 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
use stable_mir::ty::{Abi, FnSig};
FnSig {
inputs_and_output: self
.inputs_and_output
.iter()
.map(|ty| tables.intern_ty(ty))
.collect(),
inputs_and_output: self.inputs_and_output.iter().map(|ty| ty.stable(tables)).collect(),
c_variadic: self.c_variadic,
unsafety: self.unsafety.stable(tables),
abi: match self.abi {
@ -1241,9 +1202,16 @@ impl<'tcx> Stable<'tcx> for hir::Movability {
}
impl<'tcx> Stable<'tcx> for Ty<'tcx> {
type T = stable_mir::ty::Ty;
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
tables.intern_ty(*self)
}
}
impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> {
type T = stable_mir::ty::TyKind;
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
match self.kind() {
match self {
ty::Bool => TyKind::RigidTy(RigidTy::Bool),
ty::Char => TyKind::RigidTy(RigidTy::Char),
ty::Int(int_ty) => TyKind::RigidTy(RigidTy::Int(int_ty.stable(tables))),
@ -1256,15 +1224,15 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> {
ty::Foreign(def_id) => TyKind::RigidTy(RigidTy::Foreign(tables.foreign_def(*def_id))),
ty::Str => TyKind::RigidTy(RigidTy::Str),
ty::Array(ty, constant) => {
TyKind::RigidTy(RigidTy::Array(tables.intern_ty(*ty), constant.stable(tables)))
TyKind::RigidTy(RigidTy::Array(ty.stable(tables), constant.stable(tables)))
}
ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(tables.intern_ty(*ty))),
ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables))),
ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
TyKind::RigidTy(RigidTy::RawPtr(tables.intern_ty(*ty), mutbl.stable(tables)))
TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables), mutbl.stable(tables)))
}
ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref(
region.stable(tables),
tables.intern_ty(*ty),
ty.stable(tables),
mutbl.stable(tables),
)),
ty::FnDef(def_id, generic_args) => {
@ -1291,9 +1259,9 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> {
movability.stable(tables),
)),
ty::Never => TyKind::RigidTy(RigidTy::Never),
ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple(
fields.iter().map(|ty| tables.intern_ty(ty)).collect(),
)),
ty::Tuple(fields) => {
TyKind::RigidTy(RigidTy::Tuple(fields.iter().map(|ty| ty.stable(tables)).collect()))
}
ty::Alias(alias_kind, alias_ty) => {
TyKind::Alias(alias_kind.stable(tables), alias_ty.stable(tables))
}
@ -1312,32 +1280,36 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
type T = stable_mir::ty::Const;
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
stable_mir::ty::Const {
literal: match self.kind() {
ty::Value(val) => {
let const_val = tables.tcx.valtree_to_const_val((self.ty(), val));
let kind = match self.kind() {
ty::Value(val) => {
let const_val = tables.tcx.valtree_to_const_val((self.ty(), val));
if matches!(const_val, mir::ConstValue::ZeroSized) {
ConstantKind::ZeroSized
} else {
stable_mir::ty::ConstantKind::Allocated(alloc::new_allocation(
self.ty(),
const_val,
tables,
))
}
ty::ParamCt(param) => stable_mir::ty::ConstantKind::Param(param.stable(tables)),
ty::ErrorCt(_) => unreachable!(),
ty::InferCt(_) => unreachable!(),
ty::BoundCt(_, _) => unimplemented!(),
ty::PlaceholderCt(_) => unimplemented!(),
ty::Unevaluated(uv) => {
stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst {
def: tables.const_def(uv.def),
args: uv.args.stable(tables),
promoted: None,
})
}
ty::ExprCt(_) => unimplemented!(),
},
ty: tables.intern_ty(self.ty()),
}
}
ty::ParamCt(param) => stable_mir::ty::ConstantKind::Param(param.stable(tables)),
ty::ErrorCt(_) => unreachable!(),
ty::InferCt(_) => unreachable!(),
ty::BoundCt(_, _) => unimplemented!(),
ty::PlaceholderCt(_) => unimplemented!(),
ty::Unevaluated(uv) => {
stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst {
def: tables.const_def(uv.def),
args: uv.args.stable(tables),
promoted: None,
})
}
ty::ExprCt(_) => unimplemented!(),
};
let ty = self.ty().stable(tables);
let id = tables.intern_const(mir::Const::Ty(*self));
Const::new(kind, ty, id)
}
}
@ -1422,22 +1394,28 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> {
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
match *self {
mir::Const::Ty(c) => c.stable(tables),
mir::Const::Unevaluated(unev_const, ty) => stable_mir::ty::Const {
literal: stable_mir::ty::ConstantKind::Unevaluated(
stable_mir::ty::UnevaluatedConst {
mir::Const::Unevaluated(unev_const, ty) => {
let kind =
stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst {
def: tables.const_def(unev_const.def),
args: unev_const.args.stable(tables),
promoted: unev_const.promoted.map(|u| u.as_u32()),
},
),
ty: tables.intern_ty(ty),
},
mir::Const::Val(val, ty) => stable_mir::ty::Const {
literal: stable_mir::ty::ConstantKind::Allocated(alloc::new_allocation(
ty, val, tables,
)),
ty: tables.intern_ty(ty),
},
});
let ty = ty.stable(tables);
let id = tables.intern_const(*self);
Const::new(kind, ty, id)
}
mir::Const::Val(val, ty) if matches!(val, mir::ConstValue::ZeroSized) => {
let ty = ty.stable(tables);
let id = tables.intern_const(*self);
Const::new(ConstantKind::ZeroSized, ty, id)
}
mir::Const::Val(val, ty) => {
let kind = ConstantKind::Allocated(alloc::new_allocation(ty, val, tables));
let ty = ty.stable(tables);
let id = tables.intern_const(*self);
Const::new(kind, ty, id)
}
}
}
}
@ -1551,30 +1529,32 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
type T = stable_mir::ty::ClauseKind;
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
use ty::ClauseKind::*;
use ty::ClauseKind;
match *self {
Trait(trait_object) => stable_mir::ty::ClauseKind::Trait(trait_object.stable(tables)),
RegionOutlives(region_outlives) => {
ClauseKind::Trait(trait_object) => {
stable_mir::ty::ClauseKind::Trait(trait_object.stable(tables))
}
ClauseKind::RegionOutlives(region_outlives) => {
stable_mir::ty::ClauseKind::RegionOutlives(region_outlives.stable(tables))
}
TypeOutlives(type_outlives) => {
ClauseKind::TypeOutlives(type_outlives) => {
let ty::OutlivesPredicate::<_, _>(a, b) = type_outlives;
stable_mir::ty::ClauseKind::TypeOutlives(stable_mir::ty::OutlivesPredicate(
tables.intern_ty(a),
a.stable(tables),
b.stable(tables),
))
}
Projection(projection_predicate) => {
ClauseKind::Projection(projection_predicate) => {
stable_mir::ty::ClauseKind::Projection(projection_predicate.stable(tables))
}
ConstArgHasType(const_, ty) => stable_mir::ty::ClauseKind::ConstArgHasType(
ClauseKind::ConstArgHasType(const_, ty) => stable_mir::ty::ClauseKind::ConstArgHasType(
const_.stable(tables),
tables.intern_ty(ty),
ty.stable(tables),
),
WellFormed(generic_arg) => {
ClauseKind::WellFormed(generic_arg) => {
stable_mir::ty::ClauseKind::WellFormed(generic_arg.unpack().stable(tables))
}
ConstEvaluatable(const_) => {
ClauseKind::ConstEvaluatable(const_) => {
stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables))
}
}
@ -1599,7 +1579,7 @@ impl<'tcx> Stable<'tcx> for ty::SubtypePredicate<'tcx> {
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
let ty::SubtypePredicate { a, b, a_is_expected: _ } = self;
stable_mir::ty::SubtypePredicate { a: tables.intern_ty(*a), b: tables.intern_ty(*b) }
stable_mir::ty::SubtypePredicate { a: a.stable(tables), b: b.stable(tables) }
}
}
@ -1608,7 +1588,7 @@ impl<'tcx> Stable<'tcx> for ty::CoercePredicate<'tcx> {
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
let ty::CoercePredicate { a, b } = self;
stable_mir::ty::CoercePredicate { a: tables.intern_ty(*a), b: tables.intern_ty(*b) }
stable_mir::ty::CoercePredicate { a: a.stable(tables), b: b.stable(tables) }
}
}

View File

@ -713,6 +713,7 @@ symbols! {
encode,
end,
env,
env_CFG_RELEASE: env!("CFG_RELEASE"),
eprint_macro,
eprintln_macro,
eq,
@ -910,6 +911,7 @@ symbols! {
iter,
iter_mut,
iter_repeat,
iterator,
iterator_collect_fn,
kcfi,
keyword,

View File

@ -15,9 +15,7 @@ twox-hash = "1.6.3"
rustc_span = { path = "../rustc_span" }
rustc_middle = { path = "../rustc_middle" }
rustc_hir = { path = "../rustc_hir" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_target = { path = "../rustc_target" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_session = { path = "../rustc_session" }
rustc_macros = { path = "../rustc_macros" }
rustc_errors = { path = "../rustc_errors" }

View File

@ -1 +0,0 @@
symbol_mangling_test_output = {$kind}({$content})

View File

@ -1,18 +1,32 @@
//! Errors emitted by symbol_mangling.
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_macros::Diagnostic;
use rustc_errors::{ErrorGuaranteed, IntoDiagnostic};
use rustc_span::Span;
use std::fmt;
#[derive(Diagnostic)]
#[diag(symbol_mangling_test_output)]
pub struct TestOutput {
#[primary_span]
pub span: Span,
pub kind: Kind,
pub content: String,
}
// This diagnostic doesn't need translation because (a) it doesn't contain any
// natural language, and (b) it's only used in tests. So we construct it
// manually and avoid the fluent machinery.
impl IntoDiagnostic<'_> for TestOutput {
fn into_diagnostic(
self,
handler: &'_ rustc_errors::Handler,
) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
let TestOutput { span, kind, content } = self;
#[allow(rustc::untranslatable_diagnostic)]
let mut diag = handler.struct_err(format!("{kind}({content})"));
diag.set_span(span);
diag
}
}
pub enum Kind {
SymbolName,
Demangling,
@ -20,15 +34,13 @@ pub enum Kind {
DefPath,
}
impl IntoDiagnosticArg for Kind {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
let kind = match self {
Kind::SymbolName => "symbol-name",
Kind::Demangling => "demangling",
Kind::DemanglingAlt => "demangling-alt",
Kind::DefPath => "def-path",
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Kind::SymbolName => write!(f, "symbol-name"),
Kind::Demangling => write!(f, "demangling"),
Kind::DemanglingAlt => write!(f, "demangling-alt"),
Kind::DefPath => write!(f, "def-path"),
}
.into();
DiagnosticArgValue::Str(kind)
}
}

View File

@ -103,8 +103,6 @@ extern crate rustc_middle;
#[macro_use]
extern crate tracing;
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@ -121,8 +119,6 @@ pub mod errors;
pub mod test;
pub mod typeid;
fluent_messages! { "../messages.ftl" }
/// This function computes the symbol name for the given `instance` and the
/// given instantiating crate. That is, if you know that instance X is
/// instantiated in crate Y, this is the symbol name this instance would have.

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