Merge pull request #4070 from rust-lang/rustup-2024-12-04

Automatic Rustup
This commit is contained in:
Ralf Jung 2024-12-04 07:44:16 +00:00 committed by GitHub
commit 86d6dc008e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1616 changed files with 17635 additions and 11761 deletions

3
.gitignore vendored
View file

@ -46,8 +46,7 @@ no_llvm_build
/inst/
/llvm/
/mingw-build/
build/
!/compiler/rustc_mir_build/src/build/
/build
/build-rust-analyzer/
/dist/
/unicode-downloads

View file

@ -254,6 +254,7 @@ Jack Huey <jack.huey@umassmed.edu> <jackh726@gmail.com>
Jacob <jacob.macritchie@gmail.com>
Jacob Greenfield <xales@naveria.com>
Jacob Pratt <jacob@jhpratt.dev> <the.z.cuber@gmail.com>
Jacob Pratt <jacob@jhpratt.dev> <jacopratt@tesla.com>
Jake Vossen <jake@vossen.dev>
Jakob Degen <jakob.e.degen@gmail.com> <jakob@degen.com>
Jakob Lautrup Nysom <jako3047@gmail.com>

View file

@ -536,7 +536,7 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "clippy"
version = "0.1.84"
version = "0.1.85"
dependencies = [
"anstream",
"cargo_metadata",
@ -567,8 +567,9 @@ dependencies = [
[[package]]
name = "clippy_config"
version = "0.1.84"
version = "0.1.85"
dependencies = [
"clippy_utils",
"itertools",
"serde",
"toml 0.7.8",
@ -580,6 +581,7 @@ name = "clippy_dev"
version = "0.0.1"
dependencies = [
"aho-corasick",
"chrono",
"clap",
"indoc",
"itertools",
@ -590,7 +592,7 @@ dependencies = [
[[package]]
name = "clippy_lints"
version = "0.1.84"
version = "0.1.85"
dependencies = [
"arrayvec",
"cargo_metadata",
@ -613,12 +615,12 @@ dependencies = [
[[package]]
name = "clippy_utils"
version = "0.1.84"
version = "0.1.85"
dependencies = [
"arrayvec",
"clippy_config",
"itertools",
"rustc_apfloat",
"serde",
]
[[package]]
@ -1875,16 +1877,6 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jemalloc-sys"
version = "0.5.4+5.3.0-patched"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "jobserver"
version = "0.1.32"
@ -2285,7 +2277,6 @@ dependencies = [
"ctrlc",
"directories",
"getrandom",
"jemalloc-sys",
"libc",
"libffi",
"libloading",
@ -2295,6 +2286,7 @@ dependencies = [
"rustc_version",
"smallvec",
"tempfile",
"tikv-jemalloc-sys",
"ui_test",
"windows-sys 0.52.0",
]
@ -3149,12 +3141,12 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
name = "rustc-main"
version = "0.0.0"
dependencies = [
"jemalloc-sys",
"rustc_codegen_ssa",
"rustc_driver",
"rustc_driver_impl",
"rustc_smir",
"stable_mir",
"tikv-jemalloc-sys",
]
[[package]]
@ -3820,7 +3812,6 @@ dependencies = [
name = "rustc_index"
version = "0.0.0"
dependencies = [
"arrayvec",
"rustc_index_macros",
"rustc_macros",
"rustc_serialize",
@ -4137,6 +4128,7 @@ name = "rustc_monomorphize"
version = "0.0.0"
dependencies = [
"rustc_abi",
"rustc_attr",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
@ -4711,9 +4703,9 @@ checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]]
name = "ruzstd"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99c3938e133aac070997ddc684d4b393777d293ba170f2988c8fd5ea2ad4ce21"
checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f"
dependencies = [
"twox-hash",
]
@ -5258,6 +5250,16 @@ dependencies = [
name = "tier-check"
version = "0.1.0"
[[package]]
name = "tikv-jemalloc-sys"
version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "time"
version = "0.3.36"
@ -5801,17 +5803,20 @@ checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
[[package]]
name = "wasm-component-ld"
version = "0.5.10"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4aa6bd7fbe7cffbed29fe3e236fda74419def1bdef6f80f989ec51137edf44"
checksum = "a2b05c3820968b335f10e703218459e4fd2cc91fdfc8f7936a993f1aacaa0938"
dependencies = [
"anyhow",
"clap",
"lexopt",
"libc",
"tempfile",
"wasi-preview1-component-adapter-provider",
"wasmparser 0.219.1",
"wat",
"windows-sys 0.59.0",
"winsplit",
"wit-component",
"wit-parser",
]
@ -6183,6 +6188,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "winsplit"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956"
[[package]]
name = "wit-component"
version = "0.219.1"

View file

@ -43,7 +43,7 @@ Libraries
- [Document that `catch_unwind` can deal with foreign exceptions without UB, although the exact behavior is unspecified.](https://github.com/rust-lang/rust/pull/128321)
- [Implement `Default` for `HashMap`/`HashSet` iterators that don't already have it.](https://github.com/rust-lang/rust/pull/128711)
- [Bump Unicode to version 16.0.0.](https://github.com/rust-lang/rust/pull/130183)
- [Change documentation of `ptr::add`/`sub` to not claim equivalence with `offset`.](https://github.com/rust-lang/rust/pull/130229).
- [Change documentation of `ptr::add`/`sub` to not claim equivalence with `offset`.](https://github.com/rust-lang/rust/pull/130229)
<a id="1.83.0-Stabilized-APIs"></a>

View file

@ -20,14 +20,14 @@ rustc_smir = { path = "../rustc_smir" }
stable_mir = { path = "../stable_mir" }
# tidy-alphabetical-end
[dependencies.jemalloc-sys]
version = "0.5.0"
[dependencies.tikv-jemalloc-sys]
version = "0.6.0"
optional = true
features = ['unprefixed_malloc_on_supported_platforms']
[features]
# tidy-alphabetical-start
jemalloc = ['dep:jemalloc-sys']
jemalloc = ['dep:tikv-jemalloc-sys']
llvm = ['rustc_driver_impl/llvm']
max_level_info = ['rustc_driver_impl/max_level_info']
rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts']

View file

@ -43,6 +43,8 @@ fn main() {
{
use std::os::raw::{c_int, c_void};
use tikv_jemalloc_sys as jemalloc_sys;
#[used]
static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc;
#[used]

View file

@ -1215,6 +1215,15 @@ impl Scalar {
Scalar::Union { .. } => true,
}
}
/// Returns `true` if this is a signed integer scalar
#[inline]
pub fn is_signed(&self) -> bool {
match self.primitive() {
Primitive::Int(_, signed) => signed,
_ => false,
}
}
}
// NOTE: This struct is generic over the FieldIdx for rust-analyzer usage.
@ -1401,10 +1410,7 @@ impl BackendRepr {
#[inline]
pub fn is_signed(&self) -> bool {
match self {
BackendRepr::Scalar(scal) => match scal.primitive() {
Primitive::Int(_, signed) => signed,
_ => false,
},
BackendRepr::Scalar(scal) => scal.is_signed(),
_ => panic!("`is_signed` on non-scalar ABI {self:?}"),
}
}
@ -1499,7 +1505,11 @@ impl BackendRepr {
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
Single { index: VariantIdx },
Single {
/// Always 0 for non-enums/generators.
/// For enums without a variant, this is an invalid index!
index: VariantIdx,
},
/// Enum-likes with more than one variant: each variant comes with
/// a *discriminant* (usually the same as the variant index but the user can
@ -1528,14 +1538,22 @@ pub enum TagEncoding<VariantIdx: Idx> {
/// The variant `untagged_variant` contains a niche at an arbitrary
/// offset (field `tag_field` of the enum), which for a variant with
/// discriminant `d` is set to
/// `(d - niche_variants.start).wrapping_add(niche_start)`.
/// `(d - niche_variants.start).wrapping_add(niche_start)`
/// (this is wrapping arithmetic using the type of the niche field).
///
/// For example, `Option<(usize, &T)>` is represented such that
/// `None` has a null pointer for the second tuple field, and
/// `Some` is the identity function (with a non-null reference).
///
/// Other variants that are not `untagged_variant` and that are outside the `niche_variants`
/// range cannot be represented; they must be uninhabited.
Niche {
untagged_variant: VariantIdx,
/// This range *may* contain `untagged_variant`; that is then just a "dead value" and
/// not used to encode anything.
niche_variants: RangeInclusive<VariantIdx>,
/// This is inbounds of the type of the niche field
/// (not sign-extended, i.e., all bits beyond the niche field size are 0).
niche_start: u128,
},
}

View file

@ -39,9 +39,7 @@ pub use crate::format::*;
use crate::ptr::P;
use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
use crate::util::parser::{
AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_RANGE, PREC_UNAMBIGUOUS,
};
use crate::util::parser::{AssocOp, ExprPrecedence};
/// A "Label" is an identifier of some point in sources,
/// e.g. in the following code:
@ -1184,14 +1182,15 @@ pub struct Expr {
}
impl Expr {
/// Is this expr either `N`, or `{ N }`.
/// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter.
///
/// If this is not the case, name resolution does not resolve `N` when using
/// `min_const_generics` as more complex expressions are not supported.
///
/// Does not ensure that the path resolves to a const param, the caller should check this.
pub fn is_potential_trivial_const_arg(&self, strip_identity_block: bool) -> bool {
let this = if strip_identity_block { self.maybe_unwrap_block() } else { self };
/// This also does not consider macros, so it's only correct after macro-expansion.
pub fn is_potential_trivial_const_arg(&self) -> bool {
let this = self.maybe_unwrap_block();
if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg()
@ -1316,29 +1315,29 @@ impl Expr {
Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
}
pub fn precedence(&self) -> i8 {
pub fn precedence(&self) -> ExprPrecedence {
match self.kind {
ExprKind::Closure(..) => PREC_CLOSURE,
ExprKind::Closure(..) => ExprPrecedence::Closure,
ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::Yield(..)
| ExprKind::Yeet(..)
| ExprKind::Become(..) => PREC_JUMP,
| ExprKind::Become(..) => ExprPrecedence::Jump,
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
// ensures that `pprust` will add parentheses in the right places to get the desired
// parse.
ExprKind::Range(..) => PREC_RANGE,
ExprKind::Range(..) => ExprPrecedence::Range,
// Binop-like expr kinds, handled by `AssocOp`.
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::Assign(..) |
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
ExprKind::AssignOp(..) => ExprPrecedence::Assign,
// Unary, prefix
ExprKind::AddrOf(..)
@ -1347,7 +1346,7 @@ impl Expr {
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
// but we need to print `(let _ = a) < b` as-is with parens.
| ExprKind::Let(..)
| ExprKind::Unary(..) => PREC_PREFIX,
| ExprKind::Unary(..) => ExprPrecedence::Prefix,
// Never need parens
ExprKind::Array(_)
@ -1380,7 +1379,7 @@ impl Expr {
| ExprKind::Underscore
| ExprKind::While(..)
| ExprKind::Err(_)
| ExprKind::Dummy => PREC_UNAMBIGUOUS,
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
}
}
@ -1732,12 +1731,12 @@ pub enum AttrArgs {
/// Delimited arguments: `#[attr()/[]/{}]`.
Delimited(DelimArgs),
/// Arguments of a key-value attribute: `#[attr = "value"]`.
Eq(
Eq {
/// Span of the `=` token.
Span,
/// The "value".
AttrArgsEq,
),
eq_span: Span,
value: AttrArgsEq,
},
}
// The RHS of an `AttrArgs::Eq` starts out as an expression. Once macro
@ -1749,15 +1748,39 @@ pub enum AttrArgsEq {
Hir(MetaItemLit),
}
impl AttrArgsEq {
pub fn span(&self) -> Span {
match self {
AttrArgsEq::Ast(p) => p.span,
AttrArgsEq::Hir(lit) => lit.span,
}
}
pub fn unwrap_ast(&self) -> &Expr {
match self {
AttrArgsEq::Ast(p) => p,
AttrArgsEq::Hir(lit) => {
unreachable!("in literal form when getting inner tokens: {lit:?}")
}
}
}
pub fn unwrap_ast_mut(&mut self) -> &mut P<Expr> {
match self {
AttrArgsEq::Ast(p) => p,
AttrArgsEq::Hir(lit) => {
unreachable!("in literal form when getting inner tokens: {lit:?}")
}
}
}
}
impl AttrArgs {
pub fn span(&self) -> Option<Span> {
match self {
AttrArgs::Empty => None,
AttrArgs::Delimited(args) => Some(args.dspan.entire()),
AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => Some(eq_span.to(expr.span)),
AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => {
unreachable!("in literal form when getting span: {:?}", lit);
}
AttrArgs::Eq { eq_span, value } => Some(eq_span.to(value.span())),
}
}
@ -1767,10 +1790,7 @@ impl AttrArgs {
match self {
AttrArgs::Empty => TokenStream::default(),
AttrArgs::Delimited(args) => args.tokens.clone(),
AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => TokenStream::from_ast(expr),
AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => {
unreachable!("in literal form when getting inner tokens: {:?}", lit)
}
AttrArgs::Eq { value, .. } => TokenStream::from_ast(value.unwrap_ast()),
}
}
}
@ -1784,10 +1804,10 @@ where
match self {
AttrArgs::Empty => {}
AttrArgs::Delimited(args) => args.hash_stable(ctx, hasher),
AttrArgs::Eq(_eq_span, AttrArgsEq::Ast(expr)) => {
AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => {
unreachable!("hash_stable {:?}", expr);
}
AttrArgs::Eq(eq_span, AttrArgsEq::Hir(lit)) => {
AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) } => {
eq_span.hash_stable(ctx, hasher);
lit.hash_stable(ctx, hasher);
}

View file

@ -250,7 +250,7 @@ impl AttrItem {
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
MetaItemKind::list_from_tokens(args.tokens.clone())
}
AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None,
AttrArgs::Delimited(_) | AttrArgs::Eq { .. } | AttrArgs::Empty => None,
}
}
@ -268,7 +268,7 @@ impl AttrItem {
/// ```
fn value_str(&self) -> Option<Symbol> {
match &self.args {
AttrArgs::Eq(_, args) => args.value_str(),
AttrArgs::Eq { value, .. } => value.value_str(),
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
}
}
@ -492,7 +492,7 @@ impl MetaItemKind {
MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List)
}
AttrArgs::Delimited(..) => None,
AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => match expr.kind {
ExprKind::Lit(token_lit) => {
// Turn failures to `None`, we'll get parse errors elsewhere.
MetaItemLit::from_token_lit(token_lit, expr.span)
@ -501,7 +501,9 @@ impl MetaItemKind {
}
_ => None,
},
AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => {
Some(MetaItemKind::NameValue(lit.clone()))
}
}
}
}
@ -702,7 +704,7 @@ pub fn mk_attr_name_value_str(
tokens: None,
});
let path = Path::from_ident(Ident::new(name, span));
let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
let args = AttrArgs::Eq { eq_span: span, value: AttrArgsEq::Ast(expr) };
mk_attr(g, style, unsafety, path, args, span)
}

View file

@ -451,13 +451,10 @@ fn visit_attr_args<T: MutVisitor>(vis: &mut T, args: &mut AttrArgs) {
match args {
AttrArgs::Empty => {}
AttrArgs::Delimited(args) => visit_delim_args(vis, args),
AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => {
vis.visit_expr(expr);
AttrArgs::Eq { eq_span, value } => {
vis.visit_expr(value.unwrap_ast_mut());
vis.visit_span(eq_span);
}
AttrArgs::Eq(_eq_span, AttrArgsEq::Hir(lit)) => {
unreachable!("in literal form when visiting mac args eq: {:?}", lit)
}
}
}

View file

@ -128,21 +128,21 @@ impl AssocOp {
}
/// Gets the precedence of this operator
pub fn precedence(&self) -> usize {
pub fn precedence(&self) -> ExprPrecedence {
use AssocOp::*;
match *self {
As => 14,
Multiply | Divide | Modulus => 13,
Add | Subtract => 12,
ShiftLeft | ShiftRight => 11,
BitAnd => 10,
BitXor => 9,
BitOr => 8,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
LAnd => 6,
LOr => 5,
DotDot | DotDotEq => 4,
Assign | AssignOp(_) => 2,
As => ExprPrecedence::Cast,
Multiply | Divide | Modulus => ExprPrecedence::Product,
Add | Subtract => ExprPrecedence::Sum,
ShiftLeft | ShiftRight => ExprPrecedence::Shift,
BitAnd => ExprPrecedence::BitAnd,
BitXor => ExprPrecedence::BitXor,
BitOr => ExprPrecedence::BitOr,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
LAnd => ExprPrecedence::LAnd,
LOr => ExprPrecedence::LOr,
DotDot | DotDotEq => ExprPrecedence::Range,
Assign | AssignOp(_) => ExprPrecedence::Assign,
}
}
@ -229,17 +229,44 @@ impl AssocOp {
}
}
pub const PREC_CLOSURE: i8 = -40;
pub const PREC_JUMP: i8 = -30;
pub const PREC_RANGE: i8 = -10;
// The range 2..=14 is reserved for AssocOp binary operator precedences.
pub const PREC_PREFIX: i8 = 50;
pub const PREC_UNAMBIGUOUS: i8 = 60;
pub const PREC_FORCE_PAREN: i8 = 100;
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub enum ExprPrecedence {
Closure,
// return, break, yield
Jump,
// = += -= *= /= %= &= |= ^= <<= >>=
Assign,
// .. ..=
Range,
// ||
LOr,
// &&
LAnd,
// == != < > <= >=
Compare,
// |
BitOr,
// ^
BitXor,
// &
BitAnd,
// << >>
Shift,
// + -
Sum,
// * / %
Product,
// as
Cast,
// unary - * ! & &mut
Prefix,
// paths, loops, function calls, array indexing, field expressions, method calls
Unambiguous,
}
/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
pub fn prec_let_scrutinee_needs_par() -> usize {
AssocOp::LAnd.precedence()
pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence {
ExprPrecedence::LAnd
}
/// Suppose we have `let _ = e` and the `order` of `e`.
@ -247,8 +274,8 @@ pub fn prec_let_scrutinee_needs_par() -> usize {
///
/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
/// Can we print this as `let _ = a OP b`?
pub fn needs_par_as_let_scrutinee(order: i8) -> bool {
order <= prec_let_scrutinee_needs_par() as i8
pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool {
order <= prec_let_scrutinee_needs_par()
}
/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any

View file

@ -1273,10 +1273,7 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) -
match args {
AttrArgs::Empty => {}
AttrArgs::Delimited(_args) => {}
AttrArgs::Eq(_eq_span, AttrArgsEq::Ast(expr)) => try_visit!(visitor.visit_expr(expr)),
AttrArgs::Eq(_eq_span, AttrArgsEq::Hir(lit)) => {
unreachable!("in literal form when walking mac args eq: {:?}", lit)
}
AttrArgs::Eq { value, .. } => try_visit!(visitor.visit_expr(value.unwrap_ast())),
}
V::Result::output()
}

View file

@ -82,7 +82,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let mut clobber_abis = FxIndexMap::default();
if let Some(asm_arch) = asm_arch {
for (abi_name, abi_span) in &asm.clobber_abis {
match asm::InlineAsmClobberAbi::parse(asm_arch, &self.tcx.sess.target, *abi_name) {
match asm::InlineAsmClobberAbi::parse(
asm_arch,
&self.tcx.sess.target,
&self.tcx.sess.unstable_target_features,
*abi_name,
) {
Ok(abi) => {
// If the abi was already in the list, emit an error
match clobber_abis.get(&abi) {
@ -222,18 +227,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
// Wrap the expression in an AnonConst.
let parent_def_id = self.current_def_id_parent;
let parent_def_id = self.current_hir_id_owner.def_id;
let node_id = self.next_node_id();
// HACK(min_generic_const_args): see lower_anon_const
if !expr.is_potential_trivial_const_arg(true) {
self.create_def(
parent_def_id,
node_id,
kw::Empty,
DefKind::AnonConst,
*op_sp,
);
}
self.create_def(
parent_def_id,
node_id,
kw::Empty,
DefKind::AnonConst,
*op_sp,
);
let anon_const = AnonConst { id: node_id, value: P(expr) };
hir::InlineAsmOperand::SymFn {
anon_const: self.lower_anon_const_to_anon_const(&anon_const),

View file

@ -109,16 +109,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ConstBlock {
def_id,
hir_id: this.lower_node_id(c.id),
body: this.with_def_id_parent(def_id, |this| {
this.lower_const_body(c.value.span, Some(&c.value))
}),
body: this.lower_const_body(c.value.span, Some(&c.value)),
}
});
hir::ExprKind::ConstBlock(c)
}
ExprKind::Repeat(expr, count) => {
let expr = self.lower_expr(expr);
let count = self.lower_array_length(count);
let count = self.lower_array_length_to_const_arg(count);
hir::ExprKind::Repeat(expr, count)
}
ExprKind::Tup(elts) => hir::ExprKind::Tup(self.lower_exprs(elts)),
@ -452,15 +450,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let mut generic_args = ThinVec::new();
for (idx, arg) in args.iter().cloned().enumerate() {
if legacy_args_idx.contains(&idx) {
let parent_def_id = self.current_def_id_parent;
let parent_def_id = self.current_hir_id_owner.def_id;
let node_id = self.next_node_id();
// HACK(min_generic_const_args): see lower_anon_const
if !arg.is_potential_trivial_const_arg(true) {
// Add a definition for the in-band const def.
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span);
}
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span);
let mut visitor = WillCreateDefIdsVisitor {};
let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) {
AstP(Expr {
@ -759,19 +751,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
lifetime_elision_allowed: false,
});
let body = self.with_def_id_parent(closure_def_id, move |this| {
this.lower_body(move |this| {
this.coroutine_kind = Some(coroutine_kind);
let body = self.lower_body(move |this| {
this.coroutine_kind = Some(coroutine_kind);
let old_ctx = this.task_context;
if task_context.is_some() {
this.task_context = task_context;
}
let res = body(this);
this.task_context = old_ctx;
let old_ctx = this.task_context;
if task_context.is_some() {
this.task_context = task_context;
}
let res = body(this);
this.task_context = old_ctx;
(params, res)
})
(params, res)
});
// `static |<_task_context?>| -> <return_ty> { <body> }`:
@ -1056,26 +1046,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
this.with_def_id_parent(closure_def_id, move |this| {
let mut coroutine_kind = if this
.attrs
.get(&closure_hir_id.local_id)
.is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine)))
{
Some(hir::CoroutineKind::Coroutine(Movability::Movable))
} else {
None
};
let body_id = this.lower_fn_body(decl, |this| {
this.coroutine_kind = coroutine_kind;
let e = this.lower_expr_mut(body);
coroutine_kind = this.coroutine_kind;
e
});
let coroutine_option =
this.closure_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability);
(body_id, coroutine_option)
})
let mut coroutine_kind = if this
.attrs
.get(&closure_hir_id.local_id)
.is_some_and(|attrs| attrs.iter().any(|attr| attr.has_name(sym::coroutine)))
{
Some(hir::CoroutineKind::Coroutine(Movability::Movable))
} else {
None
};
let body_id = this.lower_fn_body(decl, |this| {
this.coroutine_kind = coroutine_kind;
let e = this.lower_expr_mut(body);
coroutine_kind = this.coroutine_kind;
e
});
let coroutine_option =
this.closure_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability);
(body_id, coroutine_option)
});
let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
@ -1165,28 +1153,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
let body = self.with_new_scopes(fn_decl_span, |this| {
this.with_def_id_parent(closure_def_id, |this| {
let inner_decl =
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
let inner_decl =
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
// Transform `async |x: u8| -> X { ... }` into
// `|x: u8| || -> X { ... }`.
let body_id = this.lower_body(|this| {
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
&inner_decl,
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
fn_decl_span,
body.span,
coroutine_kind,
hir::CoroutineSource::Closure,
);
// Transform `async |x: u8| -> X { ... }` into
// `|x: u8| || -> X { ... }`.
let body_id = this.lower_body(|this| {
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
&inner_decl,
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
fn_decl_span,
body.span,
coroutine_kind,
hir::CoroutineSource::Closure,
);
this.maybe_forward_track_caller(body.span, closure_hir_id, expr.hir_id);
this.maybe_forward_track_caller(body.span, closure_hir_id, expr.hir_id);
(parameters, expr)
});
body_id
})
(parameters, expr)
});
body_id
});
let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);

View file

@ -235,8 +235,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
}
fn visit_anon_const(&mut self, constant: &'hir AnonConst) {
// FIXME: use real span?
self.insert(DUMMY_SP, constant.hir_id, Node::AnonConst(constant));
self.insert(constant.span, constant.hir_id, Node::AnonConst(constant));
self.with_parent(constant.hir_id, |this| {
intravisit::walk_anon_const(this, constant);
@ -252,8 +251,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
}
fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir>) {
// FIXME: use real span?
self.insert(DUMMY_SP, const_arg.hir_id, Node::ConstArg(const_arg));
self.insert(const_arg.span(), const_arg.hir_id, Node::ConstArg(const_arg));
self.with_parent(const_arg.hir_id, |this| {
intravisit::walk_const_arg(this, const_arg);
@ -387,13 +385,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
fn visit_array_length(&mut self, len: &'hir ArrayLen<'hir>) {
match len {
ArrayLen::Infer(inf) => self.insert(inf.span, inf.hir_id, Node::ArrayLenInfer(inf)),
ArrayLen::Body(..) => intravisit::walk_array_len(self, len),
}
}
fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) {
self.visit_pat(p)
}

View file

@ -117,18 +117,6 @@ struct LoweringContext<'a, 'hir> {
is_in_dyn_type: bool,
current_hir_id_owner: hir::OwnerId,
/// Why do we need this in addition to [`Self::current_hir_id_owner`]?
///
/// Currently (as of June 2024), anonymous constants are not HIR owners; however,
/// they do get their own DefIds. Some of these DefIds have to be created during
/// AST lowering, rather than def collection, because we can't tell until after
/// name resolution whether an anonymous constant will end up instead being a
/// [`hir::ConstArgKind::Path`]. However, to compute which generics are
/// available to an anonymous constant nested inside another, we need to make
/// sure that the parent is recorded as the parent anon const, not the enclosing
/// item. So we need to track parent defs differently from HIR owners, since they
/// will be finer-grained in the case of anon consts.
current_def_id_parent: LocalDefId,
item_local_id_counter: hir::ItemLocalId,
trait_map: ItemLocalMap<Box<[TraitCandidate]>>,
@ -161,7 +149,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
attrs: SortedMap::default(),
children: Vec::default(),
current_hir_id_owner: hir::CRATE_OWNER_ID,
current_def_id_parent: CRATE_DEF_ID,
item_local_id_counter: hir::ItemLocalId::ZERO,
ident_and_label_to_local_id: Default::default(),
#[cfg(debug_assertions)]
@ -565,7 +552,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
debug_assert_eq!(_old, None);
}
let item = self.with_def_id_parent(def_id, f);
let item = f(self);
debug_assert_eq!(def_id, item.def_id().def_id);
// `f` should have consumed all the elements in these vectors when constructing `item`.
debug_assert!(self.impl_trait_defs.is_empty());
@ -590,13 +577,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.children.push((def_id, hir::MaybeOwner::Owner(info)));
}
fn with_def_id_parent<T>(&mut self, parent: LocalDefId, f: impl FnOnce(&mut Self) -> T) -> T {
let current_def_id_parent = std::mem::replace(&mut self.current_def_id_parent, parent);
let result = f(self);
self.current_def_id_parent = current_def_id_parent;
result
}
fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> {
let attrs = std::mem::take(&mut self.attrs);
let mut bodies = std::mem::take(&mut self.bodies);
@ -773,7 +753,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
LifetimeRes::Fresh { param, kind, .. } => {
// Late resolution delegates to us the creation of the `LocalDefId`.
let _def_id = self.create_def(
self.current_hir_id_owner.def_id, // FIXME: should this use self.current_def_id_parent?
self.current_hir_id_owner.def_id,
param,
kw::UnderscoreLifetime,
DefKind::LifetimeParam,
@ -909,7 +889,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// This is an inert key-value attribute - it will never be visible to macros
// after it gets lowered to HIR. Therefore, we can extract literals to handle
// nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => {
&AttrArgs::Eq { eq_span, ref value } => {
let expr = value.unwrap_ast();
// In valid code the value always ends up as a single literal. Otherwise, a dummy
// literal suffices because the error is handled elsewhere.
let lit = if let ExprKind::Lit(token_lit) = expr.kind
@ -925,10 +906,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: DUMMY_SP,
}
};
AttrArgs::Eq(*eq_span, AttrArgsEq::Hir(lit))
}
AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => {
unreachable!("in literal form when lowering mac args eq: {:?}", lit)
AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) }
}
}
}
@ -1277,9 +1255,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}),
))
}
TyKind::Array(ty, length) => {
hir::TyKind::Array(self.lower_ty(ty, itctx), self.lower_array_length(length))
}
TyKind::Array(ty, length) => hir::TyKind::Array(
self.lower_ty(ty, itctx),
self.lower_array_length_to_const_arg(length),
),
TyKind::Typeof(expr) => hir::TyKind::Typeof(self.lower_anon_const_to_anon_const(expr)),
TyKind::TraitObject(bounds, kind) => {
let mut lifetime_bound = None;
@ -1466,17 +1445,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let opaque_ty_hir_id = self.lower_node_id(opaque_ty_node_id);
debug!(?opaque_ty_def_id, ?opaque_ty_hir_id);
let opaque_ty_def = self.with_def_id_parent(opaque_ty_def_id, |this| {
let bounds = lower_item_bounds(this);
let opaque_ty_def = hir::OpaqueTy {
hir_id: opaque_ty_hir_id,
def_id: opaque_ty_def_id,
bounds,
origin,
span: this.lower_span(opaque_ty_span),
};
this.arena.alloc(opaque_ty_def)
});
let bounds = lower_item_bounds(self);
let opaque_ty_def = hir::OpaqueTy {
hir_id: opaque_ty_hir_id,
def_id: opaque_ty_def_id,
bounds,
origin,
span: self.lower_span(opaque_ty_span),
};
let opaque_ty_def = self.arena.alloc(opaque_ty_def);
hir::TyKind::OpaqueDef(opaque_ty_def)
}
@ -2029,14 +2006,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.expr_block(block)
}
fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen<'hir> {
fn lower_array_length_to_const_arg(&mut self, c: &AnonConst) -> &'hir hir::ConstArg<'hir> {
match c.value.kind {
ExprKind::Underscore => {
if self.tcx.features().generic_arg_infer() {
hir::ArrayLen::Infer(hir::InferArg {
hir_id: self.lower_node_id(c.id),
span: self.lower_span(c.value.span),
})
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span));
self.arena
.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
} else {
feature_err(
&self.tcx.sess,
@ -2045,10 +2021,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fluent_generated::ast_lowering_underscore_array_length_unstable,
)
.stash(c.value.span, StashKey::UnderscoreForArrayLengths);
hir::ArrayLen::Body(self.lower_anon_const_to_const_arg(c))
self.lower_anon_const_to_const_arg(c)
}
}
_ => hir::ArrayLen::Body(self.lower_anon_const_to_const_arg(c)),
_ => self.lower_anon_const_to_const_arg(c),
}
}
@ -2084,7 +2060,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else {
// Construct an AnonConst where the expr is the "ty"'s path.
let parent_def_id = self.current_def_id_parent;
let parent_def_id = self.current_hir_id_owner.def_id;
let node_id = self.next_node_id();
let span = self.lower_span(span);
@ -2108,9 +2084,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.arena.alloc(hir::AnonConst {
def_id,
hir_id,
body: this.with_def_id_parent(def_id, |this| {
this.lower_const_body(path_expr.span, Some(&path_expr))
}),
body: this.lower_const_body(path_expr.span, Some(&path_expr)),
span,
})
});
@ -2159,7 +2133,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
None,
);
return ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Path(qpath) };
return ConstArg {
hir_id: self.lower_node_id(anon.id),
kind: hir::ConstArgKind::Path(qpath),
};
}
let lowered_anon = self.lower_anon_const_to_anon_const(anon);
@ -2169,29 +2146,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// See [`hir::ConstArg`] for when to use this function vs
/// [`Self::lower_anon_const_to_const_arg`].
fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst {
if c.value.is_potential_trivial_const_arg(true) {
// HACK(min_generic_const_args): see DefCollector::visit_anon_const
// Over there, we guess if this is a bare param and only create a def if
// we think it's not. However we may can guess wrong (see there for example)
// in which case we have to create the def here.
self.create_def(
self.current_def_id_parent,
c.id,
kw::Empty,
DefKind::AnonConst,
c.value.span,
);
}
self.arena.alloc(self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
let hir_id = this.lower_node_id(c.id);
hir::AnonConst {
def_id,
hir_id,
body: this.with_def_id_parent(def_id, |this| {
this.lower_const_body(c.value.span, Some(&c.value))
}),
body: this.lower_const_body(c.value.span, Some(&c.value)),
span: this.lower_span(c.value.span),
}
}))

View file

@ -4,9 +4,9 @@ use rustc_ast::{NodeId, PatKind, attr, token};
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue};
use rustc_session::Session;
use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
use rustc_span::Span;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};
use rustc_span::symbol::{Symbol, sym};
use rustc_target::spec::abi;
use thin_vec::ThinVec;
@ -516,6 +516,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
"async closures are unstable",
"to use an async block, remove the `||`: `async {`"
);
gate_all!(
async_trait_bounds,
"`async` trait bounds are unstable",
"use the desugared name of the async trait, such as `AsyncFn`"
);
gate_all!(async_for_loop, "`for await` loops are experimental");
gate_all!(
closure_lifetime_binder,
@ -690,6 +695,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
.find(|feat| feat.gate_name == sym::generic_const_exprs)
.map(|feat| feat.attr_sp)
{
#[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))]
sess.dcx().emit_err(errors::IncompatibleFeatures {
spans: vec![gce_span],
f1: Symbol::intern("-Znext-solver=globally"),

View file

@ -648,14 +648,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
AttrArgs::Empty => {
self.print_path(&item.path, false, 0);
}
AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => {
AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => {
self.print_path(&item.path, false, 0);
self.space();
self.word_space("=");
let token_str = self.expr_to_string(expr);
self.word(token_str);
}
AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => {
AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => {
self.print_path(&item.path, false, 0);
self.space();
self.word_space("=");

View file

@ -5,7 +5,7 @@ use itertools::{Itertools, Position};
use rustc_ast::ptr::P;
use rustc_ast::util::classify;
use rustc_ast::util::literal::escape_byte_str_symbol;
use rustc_ast::util::parser::{self, AssocOp, Fixity};
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
use rustc_ast::{
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
FormatDebugHex, FormatSign, FormatTrait, token,
@ -58,10 +58,6 @@ impl<'a> State<'a> {
self.pclose()
}
fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8, fixup: FixupContext) {
self.print_expr_cond_paren(expr, expr.precedence() < prec, fixup);
}
/// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
/// `if cond { ... }`.
fn print_expr_as_cond(&mut self, expr: &ast::Expr) {
@ -216,9 +212,9 @@ impl<'a> State<'a> {
}
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
let prec = match func.kind {
ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
_ => parser::PREC_UNAMBIGUOUS,
let needs_paren = match func.kind {
ast::ExprKind::Field(..) => true,
_ => func.precedence() < ExprPrecedence::Unambiguous,
};
// Independent of parenthesization related to precedence, we must
@ -237,7 +233,7 @@ impl<'a> State<'a> {
// because the latter is valid syntax but with the incorrect meaning.
// It's a match-expression followed by tuple-expression, not a function
// call.
self.print_expr_maybe_paren(func, prec, fixup.leftmost_subexpression());
self.print_expr_cond_paren(func, needs_paren, fixup.leftmost_subexpression());
self.print_call_post(args)
}
@ -258,7 +254,11 @@ impl<'a> State<'a> {
// boundaries, `$receiver.method()` can be parsed back as a statement
// containing an expression if and only if `$receiver` can be parsed as
// a statement containing an expression.
self.print_expr_maybe_paren(receiver, parser::PREC_UNAMBIGUOUS, fixup);
self.print_expr_cond_paren(
receiver,
receiver.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word(".");
self.print_ident(segment.ident);
@ -276,21 +276,22 @@ impl<'a> State<'a> {
fixup: FixupContext,
) {
let assoc_op = AssocOp::from_ast_binop(op.node);
let prec = assoc_op.precedence() as i8;
let fixity = assoc_op.fixity();
let binop_prec = assoc_op.precedence();
let left_prec = lhs.precedence();
let right_prec = rhs.precedence();
let (left_prec, right_prec) = match fixity {
Fixity::Left => (prec, prec + 1),
Fixity::Right => (prec + 1, prec),
Fixity::None => (prec + 1, prec + 1),
let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
};
let left_prec = match (&lhs.kind, op.node) {
match (&lhs.kind, op.node) {
// These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
// the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
// of `(x as i32) < ...`. We need to convince it _not_ to do that.
(&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => {
parser::PREC_FORCE_PAREN
left_needs_paren = true;
}
// We are given `(let _ = a) OP b`.
//
@ -300,23 +301,25 @@ impl<'a> State<'a> {
// - Otherwise, e.g. when we have `(let a = b) < c` in AST,
// parens are required since the parser would interpret `let a = b < c` as
// `let a = (b < c)`. To achieve this, we force parens.
(&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
parser::PREC_FORCE_PAREN
(&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => {
left_needs_paren = true;
}
_ => left_prec,
};
self.print_expr_maybe_paren(lhs, left_prec, fixup.leftmost_subexpression());
_ => {}
}
self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
self.space();
self.word_space(op.node.as_str());
self.print_expr_maybe_paren(rhs, right_prec, fixup.subsequent_subexpression());
self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
}
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
self.word(op.as_str());
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression());
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Prefix,
fixup.subsequent_subexpression(),
);
}
fn print_expr_addr_of(
@ -334,7 +337,11 @@ impl<'a> State<'a> {
self.print_mutability(mutability, true);
}
}
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression());
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Prefix,
fixup.subsequent_subexpression(),
);
}
pub(super) fn print_expr(&mut self, expr: &ast::Expr, fixup: FixupContext) {
@ -416,8 +423,11 @@ impl<'a> State<'a> {
self.print_token_literal(lit, expr.span)
}
ast::ExprKind::Cast(expr, ty) => {
let prec = AssocOp::As.precedence() as i8;
self.print_expr_maybe_paren(expr, prec, fixup.leftmost_subexpression());
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Cast,
fixup.leftmost_subexpression(),
);
self.space();
self.word_space("as");
self.print_type(ty);
@ -490,7 +500,11 @@ impl<'a> State<'a> {
self.space();
}
MatchKind::Postfix => {
self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word_nbsp(".match");
}
}
@ -550,33 +564,57 @@ impl<'a> State<'a> {
self.print_block_with_attrs(blk, attrs);
}
ast::ExprKind::Await(expr, _) => {
self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word(".await");
}
ast::ExprKind::Assign(lhs, rhs, _) => {
let prec = AssocOp::Assign.precedence() as i8;
self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression());
self.print_expr_cond_paren(
lhs,
// Ranges are allowed on the right-hand side of assignment,
// but not the left. `(a..b) = c` needs parentheses.
lhs.precedence() <= ExprPrecedence::Range,
fixup.leftmost_subexpression(),
);
self.space();
self.word_space("=");
self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression());
self.print_expr_cond_paren(
rhs,
rhs.precedence() < ExprPrecedence::Assign,
fixup.subsequent_subexpression(),
);
}
ast::ExprKind::AssignOp(op, lhs, rhs) => {
let prec = AssocOp::Assign.precedence() as i8;
self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression());
self.print_expr_cond_paren(
lhs,
lhs.precedence() <= ExprPrecedence::Range,
fixup.leftmost_subexpression(),
);
self.space();
self.word(op.node.as_str());
self.word_space("=");
self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression());
self.print_expr_cond_paren(
rhs,
rhs.precedence() < ExprPrecedence::Assign,
fixup.subsequent_subexpression(),
);
}
ast::ExprKind::Field(expr, ident) => {
self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word(".");
self.print_ident(*ident);
}
ast::ExprKind::Index(expr, index, _) => {
self.print_expr_maybe_paren(
self.print_expr_cond_paren(
expr,
parser::PREC_UNAMBIGUOUS,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup.leftmost_subexpression(),
);
self.word("[");
@ -588,16 +626,24 @@ impl<'a> State<'a> {
// than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
// Here we use a fake precedence value so that any child with lower precedence than
// a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
let fake_prec = AssocOp::LOr.precedence() as i8;
let fake_prec = ExprPrecedence::LOr;
if let Some(e) = start {
self.print_expr_maybe_paren(e, fake_prec, fixup.leftmost_subexpression());
self.print_expr_cond_paren(
e,
e.precedence() < fake_prec,
fixup.leftmost_subexpression(),
);
}
match limits {
ast::RangeLimits::HalfOpen => self.word(".."),
ast::RangeLimits::Closed => self.word("..="),
}
if let Some(e) = end {
self.print_expr_maybe_paren(e, fake_prec, fixup.subsequent_subexpression());
self.print_expr_cond_paren(
e,
e.precedence() < fake_prec,
fixup.subsequent_subexpression(),
);
}
}
ast::ExprKind::Underscore => self.word("_"),
@ -615,7 +661,7 @@ impl<'a> State<'a> {
expr,
// Parenthesize if required by precedence, or in the
// case of `break 'inner: loop { break 'inner 1 } + 1`
expr.precedence() < parser::PREC_JUMP
expr.precedence() < ExprPrecedence::Jump
|| (opt_label.is_none() && classify::leading_labeled_expr(expr)),
fixup.subsequent_subexpression(),
);
@ -632,9 +678,9 @@ impl<'a> State<'a> {
self.word("return");
if let Some(expr) = result {
self.word(" ");
self.print_expr_maybe_paren(
self.print_expr_cond_paren(
expr,
parser::PREC_JUMP,
expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(),
);
}
@ -645,9 +691,9 @@ impl<'a> State<'a> {
self.word("yeet");
if let Some(expr) = result {
self.word(" ");
self.print_expr_maybe_paren(
self.print_expr_cond_paren(
expr,
parser::PREC_JUMP,
expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(),
);
}
@ -655,9 +701,9 @@ impl<'a> State<'a> {
ast::ExprKind::Become(result) => {
self.word("become");
self.word(" ");
self.print_expr_maybe_paren(
self.print_expr_cond_paren(
result,
parser::PREC_JUMP,
result.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(),
);
}
@ -709,15 +755,15 @@ impl<'a> State<'a> {
if let Some(expr) = e {
self.space();
self.print_expr_maybe_paren(
self.print_expr_cond_paren(
expr,
parser::PREC_JUMP,
expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(),
);
}
}
ast::ExprKind::Try(e) => {
self.print_expr_maybe_paren(e, parser::PREC_UNAMBIGUOUS, fixup);
self.print_expr_cond_paren(e, e.precedence() < ExprPrecedence::Unambiguous, fixup);
self.word("?")
}
ast::ExprKind::TryBlock(blk) => {

View file

@ -34,6 +34,7 @@ use rustc_infer::infer::{
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::*;
use rustc_middle::query::Providers;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::impls::{
@ -502,7 +503,7 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
for data in tcx.typeck(def_id).concrete_opaque_types.iter().map(|(k, v)| (*k, *v)) {
// HIR typeck did not infer the regions of the opaque, so we instantiate
// them with fresh inference variables.
let (key, hidden_ty) = tcx.fold_regions(data, |_, _| {
let (key, hidden_ty) = fold_regions(tcx, data, |_, _| {
self.next_nll_region_var_in_universe(
NllRegionVariableOrigin::Existential { from_forall: false },
ty::UniverseIndex::ROOT,

View file

@ -18,6 +18,7 @@ use rustc_middle::mir::{
TerminatorKind,
};
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_span::Span;
@ -1100,7 +1101,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let ty = ty.fold_with(&mut OpaqueFolder { tcx });
let mut failed = false;
let ty = tcx.fold_regions(ty, |r, _depth| {
let ty = fold_regions(tcx, ty, |r, _depth| {
let r_vid = self.to_region_vid(r);
let r_scc = self.constraint_sccs.scc(r_vid);
@ -1273,7 +1274,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
tcx.fold_regions(value, |r, _db| {
fold_regions(tcx, value, |r, _db| {
let vid = self.to_region_vid(r);
let scc = self.constraint_sccs.scc(vid);
let repr = self.scc_representative(scc);

View file

@ -3,6 +3,7 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _};
use rustc_macros::extension;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{
self, GenericArgKind, GenericArgs, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
@ -117,7 +118,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
});
debug!(?opaque_type_key, ?arg_regions);
let concrete_type = infcx.tcx.fold_regions(concrete_type, |region, _| {
let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| {
arg_regions
.iter()
.find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid))
@ -204,7 +205,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
tcx.fold_regions(ty, |region, _| match *region {
fold_regions(tcx, ty, |region, _| match *region {
ty::ReVar(vid) => {
let scc = self.constraint_sccs.scc(vid);
@ -442,7 +443,7 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> {
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let mut seen = vec![tcx.lifetimes.re_static];
let canonical_args = tcx.fold_regions(args, |r1, _| {
let canonical_args = fold_regions(tcx, args, |r1, _| {
if r1.is_error() {
r1
} else if let Some(&r2) = seen.iter().find(|&&r2| {

View file

@ -2,6 +2,7 @@ use rustc_index::IndexSlice;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::mir::visit::{MutVisitor, TyContext};
use rustc_middle::mir::{Body, ConstOperand, Location, Promoted};
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable};
use rustc_span::Symbol;
use tracing::{debug, instrument};
@ -68,7 +69,7 @@ impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> {
F: Fn() -> RegionCtxt,
{
let origin = NllRegionVariableOrigin::Existential { from_forall: false };
self.infcx.tcx.fold_regions(value, |_region, _depth| {
fold_regions(self.infcx.tcx, value, |_region, _depth| {
self.infcx.next_nll_region_var(origin, || region_ctxt_fn())
})
}

View file

@ -7,6 +7,7 @@ use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast};
use rustc_span::Span;
use rustc_span::def_id::DefId;
use rustc_trait_selection::solve::NoSolution;
use rustc_trait_selection::traits::ObligationCause;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
@ -177,6 +178,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
if self.infcx.next_trait_solver() {
let body = self.body;
let param_env = self.infcx.param_env;
// FIXME: Make this into a real type op?
self.fully_perform_op(
location.to_locations(),
ConstraintCategory::Boring,
@ -213,6 +215,40 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
#[instrument(skip(self), level = "debug")]
pub(super) fn structurally_resolve(
&mut self,
ty: Ty<'tcx>,
location: impl NormalizeLocation,
) -> Ty<'tcx> {
if self.infcx.next_trait_solver() {
let body = self.body;
let param_env = self.infcx.param_env;
// FIXME: Make this into a real type op?
self.fully_perform_op(
location.to_locations(),
ConstraintCategory::Boring,
CustomTypeOp::new(
|ocx| {
ocx.structurally_normalize(
&ObligationCause::misc(
location.to_locations().span(body),
body.source.def_id().expect_local(),
),
param_env,
ty,
)
.map_err(|_| NoSolution)
},
"normalizing struct tail",
),
)
.unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar))
} else {
self.normalize(ty, location)
}
}
#[instrument(skip(self), level = "debug")]
pub(super) fn ascribe_user_type(
&mut self,

View file

@ -8,6 +8,7 @@ use rustc_middle::bug;
use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory};
use rustc_middle::traits::ObligationCause;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_span::Span;
use rustc_trait_selection::traits::ScrubbedTraitError;
@ -216,7 +217,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
/// are dealt with during trait solving.
fn replace_placeholders_with_nll<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T {
if value.has_placeholders() {
self.tcx.fold_regions(value, |r, _| match *r {
fold_regions(self.tcx, value, |r, _| match *r {
ty::RePlaceholder(placeholder) => {
self.constraints.placeholder_region(self.infcx, placeholder)
}

View file

@ -26,6 +26,7 @@ use rustc_middle::mir::*;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{
self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt,
@ -213,7 +214,7 @@ pub(crate) fn type_check<'a, 'tcx>(
// Convert all regions to nll vars.
let (opaque_type_key, hidden_type) =
infcx.tcx.fold_regions((opaque_type_key, hidden_type), |region, _| {
fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |region, _| {
match region.kind() {
ty::ReVar(_) => region,
ty::RePlaceholder(placeholder) => {
@ -1064,7 +1065,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let tcx = self.infcx.tcx;
for proj in &user_ty.projs {
if let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() {
if !self.infcx.next_trait_solver()
&& let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind()
{
// There is nothing that we can compare here if we go through an opaque type.
// We're always in its defining scope as we can otherwise not project through
// it, so we're constraining it anyways.
@ -1075,7 +1078,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
proj,
|this, field, ()| {
let ty = this.field_ty(tcx, field);
self.normalize(ty, locations)
self.structurally_resolve(ty, locations)
},
|_, _| unreachable!(),
);
@ -2071,7 +2074,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
let unsize_to = tcx.fold_regions(ty, |r, _| {
let unsize_to = fold_regions(tcx, ty, |r, _| {
if let ty::ReVar(_) = r.kind() { tcx.lifetimes.re_erased } else { r }
});
self.prove_trait_ref(

View file

@ -26,15 +26,15 @@ use rustc_hir::lang_items::LangItem;
use rustc_index::IndexVec;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_macros::extension;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::fold::{TypeFoldable, fold_regions};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty,
TyCtxt, TypeVisitableExt,
};
use rustc_middle::{bug, span_bug};
use rustc_span::ErrorGuaranteed;
use rustc_span::symbol::{kw, sym};
use rustc_span::{ErrorGuaranteed, Symbol};
use tracing::{debug, instrument};
use crate::BorrowckInferCtxt;
@ -524,7 +524,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
let reg_vid = self
.infcx
.next_nll_region_var(FR, || RegionCtxt::Free(Symbol::intern("c-variadic")))
.next_nll_region_var(FR, || RegionCtxt::Free(sym::c_dash_variadic))
.as_var();
let region = ty::Region::new_var(self.infcx.tcx, reg_vid);
@ -540,10 +540,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
}
}
let fr_fn_body = self
.infcx
.next_nll_region_var(FR, || RegionCtxt::Free(Symbol::intern("fn_body")))
.as_var();
let fr_fn_body =
self.infcx.next_nll_region_var(FR, || RegionCtxt::Free(sym::fn_body)).as_var();
let num_universals = self.infcx.num_region_vars();
@ -824,7 +822,7 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
self.infcx.tcx.fold_regions(value, |region, _depth| {
fold_regions(self.infcx.tcx, value, |region, _depth| {
let name = region.get_name_or_anon();
debug!(?region, ?name);
@ -906,7 +904,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
tcx.fold_regions(value, |region, _| ty::Region::new_var(tcx, self.to_region_vid(region)))
fold_regions(tcx, value, |region, _| ty::Region::new_var(tcx, self.to_region_vid(region)))
}
}

View file

@ -5,10 +5,10 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(not(bootstrap), feature(autodiff))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(autodiff)]
#![feature(box_patterns)]
#![feature(decl_macro)]
#![feature(if_let_guard)]

View file

@ -74,7 +74,7 @@ fn create_jit_module(
jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8);
let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false);
let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, Symbol::intern("dummy_cgu_name"));
let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name);
crate::allocator::codegen(tcx, &mut jit_module);
@ -276,12 +276,7 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) ->
jit_module.module.prepare_for_function_redefine(func_id).unwrap();
let mut cx = crate::CodegenCx::new(
tcx,
jit_module.isa(),
false,
Symbol::intern("dummy_cgu_name"),
);
let mut cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name);
codegen_and_compile_fn(tcx, &mut cx, &mut Context::new(), jit_module, instance);
assert!(cx.global_asm.is_empty());

View file

@ -476,9 +476,14 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
let mut new_slot = |x| new_slot_fn(&mut slot_size, x);
// Allocate stack slots for saving clobbered registers
let abi_clobber = InlineAsmClobberAbi::parse(self.arch, &self.tcx.sess.target, sym::C)
.unwrap()
.clobbered_regs();
let abi_clobber = InlineAsmClobberAbi::parse(
self.arch,
&self.tcx.sess.target,
&self.tcx.sess.unstable_target_features,
sym::C,
)
.unwrap()
.clobbered_regs();
for (i, reg) in self.registers.iter().enumerate().filter_map(|(i, r)| r.map(|r| (i, r))) {
let mut need_save = true;
// If the register overlaps with a register clobbered by function call, then

View file

@ -415,7 +415,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
});
}
sym::simd_fma => {
// FIXME: simd_relaxed_fma doesn't relax to non-fused multiply-add
sym::simd_fma | sym::simd_relaxed_fma => {
intrinsic_args!(fx, args => (a, b, c); intrinsic);
if !a.layout().ty.is_simd() {

View file

@ -189,18 +189,13 @@ impl CodegenBackend for CraneliftCodegenBackend {
// FIXME return the actually used target features. this is necessary for #[cfg(target_feature)]
if sess.target.arch == "x86_64" && sess.target.os != "none" {
// x86_64 mandates SSE2 support
vec![Symbol::intern("fxsr"), sym::sse, Symbol::intern("sse2")]
vec![sym::fsxr, sym::sse, sym::sse2]
} else if sess.target.arch == "aarch64" {
match &*sess.target.os {
"none" => vec![],
// On macOS the aes, sha2 and sha3 features are enabled by default and ring
// fails to compile on macOS when they are not present.
"macos" => vec![
sym::neon,
Symbol::intern("aes"),
Symbol::intern("sha2"),
Symbol::intern("sha3"),
],
"macos" => vec![sym::neon, sym::aes, sym::sha2, sym::sha3],
// AArch64 mandates Neon support
_ => vec![sym::neon],
}

View file

@ -634,6 +634,9 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w",
InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::preg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f",
InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r",
@ -653,9 +656,9 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b",
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f",
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v",
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
| InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer)
| InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => {
| InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r",
@ -720,6 +723,9 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
cx.type_vector(cx.type_i64(), 2)
}
InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::preg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(),
InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
@ -730,9 +736,11 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => {
cx.type_vector(cx.type_i32(), 4)
}
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
| InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer)
| InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => {
| InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),

View file

@ -772,6 +772,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
sym::simd_flog => "log",
sym::simd_floor => "floor",
sym::simd_fma => "fma",
sym::simd_relaxed_fma => "fma", // FIXME: this should relax to non-fused multiply-add when necessary
sym::simd_fpowi => "__builtin_powi",
sym::simd_fpow => "pow",
sym::simd_fsin => "sin",

View file

@ -645,6 +645,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
| Arm(ArmInlineAsmRegClass::qreg_low4) => "x",
Arm(ArmInlineAsmRegClass::dreg) | Arm(ArmInlineAsmRegClass::qreg) => "w",
Hexagon(HexagonInlineAsmRegClass::reg) => "r",
Hexagon(HexagonInlineAsmRegClass::preg) => unreachable!("clobber-only"),
LoongArch(LoongArchInlineAsmRegClass::reg) => "r",
LoongArch(LoongArchInlineAsmRegClass::freg) => "f",
Mips(MipsInlineAsmRegClass::reg) => "r",
@ -655,9 +656,8 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
PowerPC(PowerPCInlineAsmRegClass::reg) => "r",
PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b",
PowerPC(PowerPCInlineAsmRegClass::freg) => "f",
PowerPC(PowerPCInlineAsmRegClass::cr)
| PowerPC(PowerPCInlineAsmRegClass::xer)
| PowerPC(PowerPCInlineAsmRegClass::vreg) => {
PowerPC(PowerPCInlineAsmRegClass::vreg) => "v",
PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => {
unreachable!("clobber-only")
}
RiscV(RiscVInlineAsmRegClass::reg) => "r",
@ -813,6 +813,7 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
| Arm(ArmInlineAsmRegClass::qreg_low8)
| Arm(ArmInlineAsmRegClass::qreg_low4) => cx.type_vector(cx.type_i64(), 2),
Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
Hexagon(HexagonInlineAsmRegClass::preg) => unreachable!("clobber-only"),
LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(),
LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(),
Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
@ -823,9 +824,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
PowerPC(PowerPCInlineAsmRegClass::cr)
| PowerPC(PowerPCInlineAsmRegClass::xer)
| PowerPC(PowerPCInlineAsmRegClass::vreg) => {
PowerPC(PowerPCInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i32(), 4),
PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => {
unreachable!("clobber-only")
}
RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
@ -1040,6 +1040,26 @@ fn llvm_fixup_input<'ll, 'tcx>(
let value = bx.or(value, bx.const_u32(0xFFFF_0000));
bx.bitcast(value, bx.type_f32())
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F32) =>
{
let value = bx.insert_element(
bx.const_undef(bx.type_vector(bx.type_f32(), 4)),
value,
bx.const_usize(0),
);
bx.bitcast(value, bx.type_vector(bx.type_f32(), 4))
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F64) =>
{
let value = bx.insert_element(
bx.const_undef(bx.type_vector(bx.type_f64(), 2)),
value,
bx.const_usize(0),
);
bx.bitcast(value, bx.type_vector(bx.type_f64(), 2))
}
_ => value,
}
}
@ -1175,6 +1195,18 @@ fn llvm_fixup_output<'ll, 'tcx>(
let value = bx.trunc(value, bx.type_i16());
bx.bitcast(value, bx.type_f16())
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F32) =>
{
let value = bx.bitcast(value, bx.type_vector(bx.type_f32(), 4));
bx.extract_element(value, bx.const_usize(0))
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F64) =>
{
let value = bx.bitcast(value, bx.type_vector(bx.type_f64(), 2));
bx.extract_element(value, bx.const_usize(0))
}
_ => value,
}
}
@ -1299,6 +1331,16 @@ fn llvm_fixup_output_type<'ll, 'tcx>(
{
cx.type_f32()
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F32) =>
{
cx.type_vector(cx.type_f32(), 4)
}
(PowerPC(PowerPCInlineAsmRegClass::vreg), BackendRepr::Scalar(s))
if s.primitive() == Primitive::Float(Float::F64) =>
{
cx.type_vector(cx.type_f64(), 2)
}
_ => layout.llvm_type(cx),
}
}

View file

@ -148,7 +148,7 @@ fn prepare_lto(
// __llvm_profile_counter_bias is pulled in at link time by an undefined reference to
// __llvm_profile_runtime, therefore we won't know until link time if this symbol
// should have default visibility.
symbols_below_threshold.push(CString::new("__llvm_profile_counter_bias").unwrap());
symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned());
Ok((symbols_below_threshold, upstream_modules))
}

View file

@ -61,6 +61,7 @@ fn write_output_file<'ll>(
dwo_output: Option<&Path>,
file_type: llvm::FileType,
self_profiler_ref: &SelfProfilerRef,
verify_llvm_ir: bool,
) -> Result<(), FatalError> {
debug!("write_output_file output={:?} dwo_output={:?}", output, dwo_output);
unsafe {
@ -79,6 +80,7 @@ fn write_output_file<'ll>(
output_c.as_ptr(),
dwo_output_ptr,
file_type,
verify_llvm_ir,
);
// Record artifact sizes for self-profiling
@ -507,7 +509,7 @@ fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option<CString> {
}
fn get_instr_profile_output_path(config: &ModuleConfig) -> Option<CString> {
config.instrument_coverage.then(|| CString::new("default_%m_%p.profraw").unwrap())
config.instrument_coverage.then(|| c"default_%m_%p.profraw".to_owned())
}
pub(crate) unsafe fn llvm_optimize(
@ -840,6 +842,7 @@ pub(crate) unsafe fn codegen(
None,
llvm::FileType::AssemblyFile,
&cgcx.prof,
config.verify_llvm_ir,
)
})?;
}
@ -877,6 +880,7 @@ pub(crate) unsafe fn codegen(
dwo_out,
llvm::FileType::ObjectFile,
&cgcx.prof,
config.verify_llvm_ir,
)
})?;
}

View file

@ -104,7 +104,10 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
let is_hidden = if is_generic {
// This is a monomorphization of a generic function.
if !cx.tcx.sess.opts.share_generics() {
if !(cx.tcx.sess.opts.share_generics()
|| tcx.codegen_fn_attrs(instance_def_id).inline
== rustc_attr::InlineAttr::Never)
{
// When not sharing generics, all instances are in the same
// crate and have hidden visibility.
true

View file

@ -82,8 +82,8 @@ pub(crate) struct CodegenCx<'ll, 'tcx> {
pub isize_ty: &'ll Type,
/// Extra codegen state needed when coverage instrumentation is enabled.
pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>,
/// Extra per-CGU codegen state needed when coverage instrumentation is enabled.
pub coverage_cx: Option<coverageinfo::CguCoverageContext<'ll, 'tcx>>,
pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>,
eh_personality: Cell<Option<&'ll Value>>,
@ -525,7 +525,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod());
let coverage_cx =
tcx.sess.instrument_coverage().then(coverageinfo::CrateCoverageContext::new);
tcx.sess.instrument_coverage().then(coverageinfo::CguCoverageContext::new);
let dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None {
let dctx = debuginfo::CodegenUnitDebugContext::new(llmod);
@ -576,7 +576,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
/// Extra state that is only available when coverage instrumentation is enabled.
#[inline]
#[track_caller]
pub(crate) fn coverage_cx(&self) -> &coverageinfo::CrateCoverageContext<'ll, 'tcx> {
pub(crate) fn coverage_cx(&self) -> &coverageinfo::CguCoverageContext<'ll, 'tcx> {
self.coverage_cx.as_ref().expect("only called when coverage instrumentation is enabled")
}

View file

@ -1,4 +1,6 @@
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId};
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion};
use crate::coverageinfo::mapgen::LocalFileId;
/// Must match the layout of `LLVMRustCounterKind`.
#[derive(Copy, Clone, Debug)]
@ -124,23 +126,37 @@ pub(crate) struct CoverageSpan {
/// Local index into the function's local-to-global file ID table.
/// The value at that index is itself an index into the coverage filename
/// table in the CGU's `__llvm_covmap` section.
pub(crate) file_id: u32,
file_id: u32,
/// 1-based starting line of the source code span.
pub(crate) start_line: u32,
start_line: u32,
/// 1-based starting column of the source code span.
pub(crate) start_col: u32,
start_col: u32,
/// 1-based ending line of the source code span.
pub(crate) end_line: u32,
end_line: u32,
/// 1-based ending column of the source code span. High bit must be unset.
pub(crate) end_col: u32,
end_col: u32,
}
impl CoverageSpan {
pub(crate) fn from_source_region(
local_file_id: LocalFileId,
code_region: &SourceRegion,
) -> Self {
let file_id = local_file_id.as_u32();
let &SourceRegion { start_line, start_col, end_line, end_col } = code_region;
// Internally, LLVM uses the high bit of `end_col` to distinguish between
// code regions and gap regions, so it can't be used by the column number.
assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}");
Self { file_id, start_line, start_col, end_line, end_col }
}
}
/// Must match the layout of `LLVMRustCoverageCodeRegion`.
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct CodeRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) span: CoverageSpan,
pub(crate) counter: Counter,
}
@ -148,7 +164,7 @@ pub(crate) struct CodeRegion {
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct BranchRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) span: CoverageSpan,
pub(crate) true_counter: Counter,
pub(crate) false_counter: Counter,
}
@ -157,7 +173,7 @@ pub(crate) struct BranchRegion {
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct MCDCBranchRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) span: CoverageSpan,
pub(crate) true_counter: Counter,
pub(crate) false_counter: Counter,
pub(crate) mcdc_branch_params: mcdc::BranchParameters,
@ -167,6 +183,6 @@ pub(crate) struct MCDCBranchRegion {
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct MCDCDecisionRegion {
pub(crate) cov_span: CoverageSpan,
pub(crate) span: CoverageSpan,
pub(crate) mcdc_decision_params: mcdc::DecisionParameters,
}

View file

@ -1,12 +1,13 @@
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexSet;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::CoverageIdsInfo;
use rustc_middle::mir::coverage::{
CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, MappingKind, Op,
SourceRegion,
};
use rustc_middle::ty::Instance;
use rustc_span::Span;
use tracing::{debug, instrument};
use tracing::debug;
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
@ -16,17 +17,8 @@ use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
pub(crate) struct FunctionCoverageCollector<'tcx> {
/// Coverage info that was attached to this function by the instrumentor.
function_coverage_info: &'tcx FunctionCoverageInfo,
ids_info: &'tcx CoverageIdsInfo,
is_used: bool,
/// Tracks which counters have been seen, so that we can identify mappings
/// to counters that were optimized out, and set them to zero.
counters_seen: BitSet<CounterId>,
/// Contains all expression IDs that have been seen in an `ExpressionUsed`
/// coverage statement, plus all expression IDs that aren't directly used
/// by any mappings (and therefore do not have expression-used statements).
/// After MIR traversal is finished, we can conclude that any IDs missing
/// from this set must have had their statements deleted by MIR opts.
expressions_seen: BitSet<ExpressionId>,
}
impl<'tcx> FunctionCoverageCollector<'tcx> {
@ -34,21 +26,24 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
pub(crate) fn new(
instance: Instance<'tcx>,
function_coverage_info: &'tcx FunctionCoverageInfo,
ids_info: &'tcx CoverageIdsInfo,
) -> Self {
Self::create(instance, function_coverage_info, true)
Self::create(instance, function_coverage_info, ids_info, true)
}
/// Creates a new set of coverage data for an unused (never called) function.
pub(crate) fn unused(
instance: Instance<'tcx>,
function_coverage_info: &'tcx FunctionCoverageInfo,
ids_info: &'tcx CoverageIdsInfo,
) -> Self {
Self::create(instance, function_coverage_info, false)
Self::create(instance, function_coverage_info, ids_info, false)
}
fn create(
instance: Instance<'tcx>,
function_coverage_info: &'tcx FunctionCoverageInfo,
ids_info: &'tcx CoverageIdsInfo,
is_used: bool,
) -> Self {
let num_counters = function_coverage_info.num_counters;
@ -58,44 +53,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
);
// Create a filled set of expression IDs, so that expressions not
// directly used by mappings will be treated as "seen".
// (If they end up being unused, LLVM will delete them for us.)
let mut expressions_seen = BitSet::new_filled(num_expressions);
// For each expression ID that is directly used by one or more mappings,
// mark it as not-yet-seen. This indicates that we expect to see a
// corresponding `ExpressionUsed` statement during MIR traversal.
for mapping in function_coverage_info.mappings.iter() {
// Currently we only worry about ordinary code mappings.
// For branch and MC/DC mappings, expressions might not correspond
// to any particular point in the control-flow graph.
// (Keep this in sync with the injection of `ExpressionUsed`
// statements in the `InstrumentCoverage` MIR pass.)
if let MappingKind::Code(term) = mapping.kind
&& let CovTerm::Expression(id) = term
{
expressions_seen.remove(id);
}
}
Self {
function_coverage_info,
is_used,
counters_seen: BitSet::new_empty(num_counters),
expressions_seen,
}
}
/// Marks a counter ID as having been seen in a counter-increment statement.
#[instrument(level = "debug", skip(self))]
pub(crate) fn mark_counter_id_seen(&mut self, id: CounterId) {
self.counters_seen.insert(id);
}
/// Marks an expression ID as having been seen in an expression-used statement.
#[instrument(level = "debug", skip(self))]
pub(crate) fn mark_expression_id_seen(&mut self, id: ExpressionId) {
self.expressions_seen.insert(id);
Self { function_coverage_info, ids_info, is_used }
}
/// Identify expressions that will always have a value of zero, and note
@ -117,7 +75,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
// (By construction, expressions can only refer to other expressions
// that have lower IDs, so one pass is sufficient.)
for (id, expression) in self.function_coverage_info.expressions.iter_enumerated() {
if !self.expressions_seen.contains(id) {
if !self.is_used || !self.ids_info.expressions_seen.contains(id) {
// If an expression was not seen, it must have been optimized away,
// so any operand that refers to it can be replaced with zero.
zero_expressions.insert(id);
@ -146,7 +104,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
assert_operand_expression_is_lower(id);
}
if is_zero_term(&self.counters_seen, &zero_expressions, *operand) {
if is_zero_term(&self.ids_info.counters_seen, &zero_expressions, *operand) {
*operand = CovTerm::Zero;
}
};
@ -172,17 +130,17 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
pub(crate) fn into_finished(self) -> FunctionCoverage<'tcx> {
let zero_expressions = self.identify_zero_expressions();
let FunctionCoverageCollector { function_coverage_info, is_used, counters_seen, .. } = self;
let FunctionCoverageCollector { function_coverage_info, ids_info, is_used, .. } = self;
FunctionCoverage { function_coverage_info, is_used, counters_seen, zero_expressions }
FunctionCoverage { function_coverage_info, ids_info, is_used, zero_expressions }
}
}
pub(crate) struct FunctionCoverage<'tcx> {
pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo,
ids_info: &'tcx CoverageIdsInfo,
is_used: bool,
counters_seen: BitSet<CounterId>,
zero_expressions: ZeroExpressions,
}
@ -220,16 +178,16 @@ impl<'tcx> FunctionCoverage<'tcx> {
})
}
/// Yields all this function's coverage mappings, after simplifying away
/// unused counters and counter expressions.
pub(crate) fn mapping_spans(
/// Converts this function's coverage mappings into an intermediate form
/// that will be used by `mapgen` when preparing for FFI.
pub(crate) fn counter_regions(
&self,
) -> impl Iterator<Item = (MappingKind, Span)> + ExactSizeIterator + Captures<'_> {
) -> impl Iterator<Item = (MappingKind, &SourceRegion)> + ExactSizeIterator {
self.function_coverage_info.mappings.iter().map(move |mapping| {
let &Mapping { ref kind, span } = mapping;
let Mapping { kind, source_region } = mapping;
let kind =
kind.map_terms(|term| if self.is_zero_term(term) { CovTerm::Zero } else { term });
(kind, span)
(kind, source_region)
})
}
@ -238,7 +196,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
}
fn is_zero_term(&self, term: CovTerm) -> bool {
is_zero_term(&self.counters_seen, &self.zero_expressions, term)
!self.is_used || is_zero_term(&self.ids_info.counters_seen, &self.zero_expressions, term)
}
}

View file

@ -1,14 +1,12 @@
mod spans;
use std::ffi::CString;
use std::sync::Arc;
use std::iter;
use itertools::Itertools as _;
use rustc_abi::Align;
use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_index::IndexVec;
use rustc_middle::mir::coverage::MappingKind;
@ -17,7 +15,7 @@ use rustc_middle::{bug, mir};
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::def_id::DefIdSet;
use rustc_span::{SourceFile, StableSourceFileId};
use rustc_span::{Span, Symbol};
use rustc_target::spec::HasTargetSpec;
use tracing::debug;
@ -74,11 +72,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
.collect::<Vec<_>>();
let all_files = function_coverage_entries
let all_file_names = function_coverage_entries
.iter()
.map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span)
.map(|span| tcx.sess.source_map().lookup_source_file(span.lo()));
let global_file_table = GlobalFileTable::new(all_files);
.map(|span| span_file_name(tcx, span));
let global_file_table = GlobalFileTable::new(all_file_names);
// Encode all filenames referenced by coverage mappings in this CGU.
let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
@ -105,8 +103,15 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
encode_mappings_for_function(tcx, &global_file_table, &function_coverage);
if coverage_mapping_buffer.is_empty() {
debug!("function has no mappings to embed; skipping");
continue;
if function_coverage.is_used() {
bug!(
"A used function should have had coverage mapping data but did not: {}",
mangled_function_name
);
} else {
debug!("unused function had no coverage mapping data: {}", mangled_function_name);
continue;
}
}
if !is_used {
@ -143,34 +148,29 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
}
}
/// Maps "global" (per-CGU) file ID numbers to their underlying source files.
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
struct GlobalFileTable {
/// This "raw" table doesn't include the working dir, so a file's
/// This "raw" table doesn't include the working dir, so a filename's
/// global ID is its index in this set **plus one**.
raw_file_table: FxIndexMap<StableSourceFileId, Arc<SourceFile>>,
raw_file_table: FxIndexSet<Symbol>,
}
impl GlobalFileTable {
fn new(all_files: impl IntoIterator<Item = Arc<SourceFile>>) -> Self {
// Collect all of the files into a set. Files usually come in contiguous
// runs, so we can dedup adjacent ones to save work.
let mut raw_file_table = all_files
.into_iter()
.dedup_by(|a, b| a.stable_id == b.stable_id)
.map(|f| (f.stable_id, f))
.collect::<FxIndexMap<StableSourceFileId, Arc<SourceFile>>>();
fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self {
// Collect all of the filenames into a set. Filenames usually come in
// contiguous runs, so we can dedup adjacent ones to save work.
let mut raw_file_table = all_file_names.into_iter().dedup().collect::<FxIndexSet<Symbol>>();
// Sort the file table by its underlying filenames.
raw_file_table.sort_unstable_by(|_, a, _, b| {
Ord::cmp(&a.name, &b.name).then_with(|| Ord::cmp(&a.stable_id, &b.stable_id))
});
// Sort the file table by its actual string values, not the arbitrary
// ordering of its symbols.
raw_file_table.sort_unstable_by(|a, b| a.as_str().cmp(b.as_str()));
Self { raw_file_table }
}
fn global_file_id_for_file(&self, file: &SourceFile) -> GlobalFileId {
let raw_id = self.raw_file_table.get_index_of(&file.stable_id).unwrap_or_else(|| {
bug!("file not found in prepared global file table: {:?}", file.name);
fn global_file_id_for_file_name(&self, file_name: Symbol) -> GlobalFileId {
let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| {
bug!("file name not found in prepared global file table: {file_name}");
});
// The raw file table doesn't include an entry for the working dir
// (which has ID 0), so add 1 to get the correct ID.
@ -178,27 +178,24 @@ impl GlobalFileTable {
}
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
let mut table = Vec::with_capacity(self.raw_file_table.len() + 1);
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
// requires setting the first filename to the compilation directory.
// Since rustc generates coverage maps with relative paths, the
// compilation directory can be combined with the relative paths
// to get absolute paths, if needed.
table.push(
tcx.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy(),
);
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
let working_dir: &str = &tcx
.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy();
// Add the regular entries after the base directory.
table.extend(self.raw_file_table.values().map(|file| {
file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy()
}));
llvm_cov::write_filenames_to_buffer(table.iter().map(|f| f.as_ref()))
// Insert the working dir at index 0, before the other filenames.
let filenames =
iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str));
llvm_cov::write_filenames_to_buffer(filenames)
}
}
@ -211,7 +208,7 @@ rustc_index::newtype_index! {
/// An index into a function's list of global file IDs. That underlying list
/// of local-to-global mappings will be embedded in the function's record in
/// the `__llvm_covfun` linker section.
struct LocalFileId {}
pub(crate) struct LocalFileId {}
}
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
@ -237,6 +234,13 @@ impl VirtualFileMapping {
}
}
fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
let source_file = tcx.sess.source_map().lookup_source_file(span.lo());
let name =
source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy();
Symbol::intern(&name)
}
/// Using the expressions and counter regions collected for a single function,
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
/// entry. The payload is returned as a vector of bytes.
@ -247,13 +251,11 @@ fn encode_mappings_for_function(
global_file_table: &GlobalFileTable,
function_coverage: &FunctionCoverage<'_>,
) -> Vec<u8> {
let mapping_spans = function_coverage.mapping_spans();
if mapping_spans.is_empty() {
let counter_regions = function_coverage.counter_regions();
if counter_regions.is_empty() {
return Vec::new();
}
let fn_cov_info = function_coverage.function_coverage_info;
let expressions = function_coverage.counter_expressions().collect::<Vec<_>>();
let mut virtual_file_mapping = VirtualFileMapping::default();
@ -263,39 +265,34 @@ fn encode_mappings_for_function(
let mut mcdc_decision_regions = vec![];
// Currently a function's mappings must all be in the same file as its body span.
let source_map = tcx.sess.source_map();
let source_file = source_map.lookup_source_file(fn_cov_info.body_span.lo());
let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);
// Look up the global file ID for that file.
let global_file_id = global_file_table.global_file_id_for_file(&source_file);
// Look up the global file ID for that filename.
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
// Associate that global file ID with a local file ID for this function.
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
let make_cov_span = |span| {
spans::make_coverage_span(local_file_id, source_map, fn_cov_info, &source_file, span)
};
// For each coverage mapping span in this function+file, convert it to a
// For each counter/region pair in this function+file, convert it to a
// form suitable for FFI.
for (mapping_kind, span) in mapping_spans {
debug!("Adding counter {mapping_kind:?} to map for {span:?}");
let Some(cov_span) = make_cov_span(span) else { continue };
for (mapping_kind, region) in counter_regions {
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
let span = ffi::CoverageSpan::from_source_region(local_file_id, region);
match mapping_kind {
MappingKind::Code(term) => {
code_regions
.push(ffi::CodeRegion { cov_span, counter: ffi::Counter::from_term(term) });
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
}
MappingKind::Branch { true_term, false_term } => {
branch_regions.push(ffi::BranchRegion {
cov_span,
span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
});
}
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
cov_span,
span,
true_counter: ffi::Counter::from_term(true_term),
false_counter: ffi::Counter::from_term(false_term),
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
@ -303,7 +300,7 @@ fn encode_mappings_for_function(
}
MappingKind::MCDCDecision(mcdc_decision_params) => {
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
cov_span,
span,
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
});
}
@ -538,9 +535,12 @@ fn add_unused_function_coverage<'tcx>(
}),
);
// An unused function's mappings will automatically be rewritten to map to
// zero, because none of its counters/expressions are marked as seen.
let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info);
// An unused function's mappings will all be rewritten to map to zero.
let function_coverage = FunctionCoverageCollector::unused(
instance,
function_coverage_info,
tcx.coverage_ids_info(instance.def),
);
cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage);
}

View file

@ -1,124 +0,0 @@
use rustc_middle::mir::coverage::FunctionCoverageInfo;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, Pos, SourceFile, Span};
use tracing::debug;
use crate::coverageinfo::ffi;
use crate::coverageinfo::mapgen::LocalFileId;
/// Converts the span into its start line and column, and end line and column.
///
/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by
/// the compiler, these column numbers are denoted in **bytes**, because that's what
/// LLVM's `llvm-cov` tool expects to see in coverage maps.
///
/// Returns `None` if the conversion failed for some reason. This shouldn't happen,
/// but it's hard to rule out entirely (especially in the presence of complex macros
/// or other expansions), and if it does happen then skipping a span or function is
/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid.
pub(crate) fn make_coverage_span(
file_id: LocalFileId,
source_map: &SourceMap,
fn_cov_info: &FunctionCoverageInfo,
file: &SourceFile,
span: Span,
) -> Option<ffi::CoverageSpan> {
let span = ensure_non_empty_span(source_map, fn_cov_info, span)?;
let lo = span.lo();
let hi = span.hi();
// Column numbers need to be in bytes, so we can't use the more convenient
// `SourceMap` methods for looking up file coordinates.
let line_and_byte_column = |pos: BytePos| -> Option<(usize, usize)> {
let rpos = file.relative_position(pos);
let line_index = file.lookup_line(rpos)?;
let line_start = file.lines()[line_index];
// Line numbers and column numbers are 1-based, so add 1 to each.
Some((line_index + 1, (rpos - line_start).to_usize() + 1))
};
let (mut start_line, start_col) = line_and_byte_column(lo)?;
let (mut end_line, end_col) = line_and_byte_column(hi)?;
// Apply an offset so that code in doctests has correct line numbers.
// FIXME(#79417): Currently we have no way to offset doctest _columns_.
start_line = source_map.doctest_offset_line(&file.name, start_line);
end_line = source_map.doctest_offset_line(&file.name, end_line);
check_coverage_span(ffi::CoverageSpan {
file_id: file_id.as_u32(),
start_line: start_line as u32,
start_col: start_col as u32,
end_line: end_line as u32,
end_col: end_col as u32,
})
}
fn ensure_non_empty_span(
source_map: &SourceMap,
fn_cov_info: &FunctionCoverageInfo,
span: Span,
) -> Option<Span> {
if !span.is_empty() {
return Some(span);
}
let lo = span.lo();
let hi = span.hi();
// The span is empty, so try to expand it to cover an adjacent '{' or '}',
// but only within the bounds of the body span.
let try_next = hi < fn_cov_info.body_span.hi();
let try_prev = fn_cov_info.body_span.lo() < lo;
if !(try_next || try_prev) {
return None;
}
source_map
.span_to_source(span, |src, start, end| try {
// We're only checking for specific ASCII characters, so we don't
// have to worry about multi-byte code points.
if try_next && src.as_bytes()[end] == b'{' {
Some(span.with_hi(hi + BytePos(1)))
} else if try_prev && src.as_bytes()[start - 1] == b'}' {
Some(span.with_lo(lo - BytePos(1)))
} else {
None
}
})
.ok()?
}
/// If `llvm-cov` sees a source region that is improperly ordered (end < start),
/// it will immediately exit with a fatal error. To prevent that from happening,
/// discard regions that are improperly ordered, or might be interpreted in a
/// way that makes them improperly ordered.
fn check_coverage_span(cov_span: ffi::CoverageSpan) -> Option<ffi::CoverageSpan> {
let ffi::CoverageSpan { file_id: _, start_line, start_col, end_line, end_col } = cov_span;
// Line/column coordinates are supposed to be 1-based. If we ever emit
// coordinates of 0, `llvm-cov` might misinterpret them.
let all_nonzero = [start_line, start_col, end_line, end_col].into_iter().all(|x| x != 0);
// Coverage mappings use the high bit of `end_col` to indicate that a
// region is actually a "gap" region, so make sure it's unset.
let end_col_has_high_bit_unset = (end_col & (1 << 31)) == 0;
// If a region is improperly ordered (end < start), `llvm-cov` will exit
// with a fatal error, which is inconvenient for users and hard to debug.
let is_ordered = (start_line, start_col) <= (end_line, end_col);
if all_nonzero && end_col_has_high_bit_unset && is_ordered {
Some(cov_span)
} else {
debug!(
?cov_span,
?all_nonzero,
?end_col_has_high_bit_unset,
?is_ordered,
"Skipping source region that would be misinterpreted or rejected by LLVM"
);
// If this happens in a debug build, ICE to make it easier to notice.
debug_assert!(false, "Improper source region: {cov_span:?}");
None
}
}

View file

@ -21,8 +21,8 @@ mod llvm_cov;
pub(crate) mod map_data;
mod mapgen;
/// A context object for maintaining all state needed by the coverageinfo module.
pub(crate) struct CrateCoverageContext<'ll, 'tcx> {
/// Extra per-CGU context/state needed for coverage instrumentation.
pub(crate) struct CguCoverageContext<'ll, 'tcx> {
/// Coverage data for each instrumented function identified by DefId.
pub(crate) function_coverage_map:
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
@ -32,7 +32,7 @@ pub(crate) struct CrateCoverageContext<'ll, 'tcx> {
covfun_section_name: OnceCell<CString>,
}
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
pub(crate) fn new() -> Self {
Self {
function_coverage_map: Default::default(),
@ -143,6 +143,13 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
let bx = self;
// Due to LocalCopy instantiation or MIR inlining, coverage statements
// can end up in a crate that isn't doing coverage instrumentation.
// When that happens, we currently just discard those statements, so
// the corresponding code will be undercounted.
// FIXME(Zalathar): Find a better solution for mixed-coverage builds.
let Some(coverage_cx) = &bx.cx.coverage_cx else { return };
let Some(function_coverage_info) =
bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
else {
@ -150,32 +157,28 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
return;
};
// FIXME(#132395): Unwrapping `coverage_cx` here has led to ICEs in the
// wild, so keep this early-return until we understand why.
let mut coverage_map = match bx.coverage_cx {
Some(ref cx) => cx.function_coverage_map.borrow_mut(),
None => return,
};
let func_coverage = coverage_map
.entry(instance)
.or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info));
// Mark the instance as used in this CGU, for coverage purposes.
// This includes functions that were not partitioned into this CGU,
// but were MIR-inlined into one of this CGU's functions.
coverage_cx.function_coverage_map.borrow_mut().entry(instance).or_insert_with(|| {
FunctionCoverageCollector::new(
instance,
function_coverage_info,
bx.tcx.coverage_ids_info(instance.def),
)
});
match *kind {
CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
"marker statement {kind:?} should have been removed by CleanupPostBorrowck"
),
CoverageKind::CounterIncrement { id } => {
func_coverage.mark_counter_id_seen(id);
// We need to explicitly drop the `RefMut` before calling into
// `instrprof_increment`, as that needs an exclusive borrow.
drop(coverage_map);
// The number of counters passed to `llvm.instrprof.increment` might
// be smaller than the number originally inserted by the instrumentor,
// if some high-numbered counters were removed by MIR optimizations.
// If so, LLVM's profiler runtime will use fewer physical counters.
let num_counters =
bx.tcx().coverage_ids_info(instance.def).max_counter_id.as_u32() + 1;
bx.tcx().coverage_ids_info(instance.def).num_counters_after_mir_opts();
assert!(
num_counters as usize <= function_coverage_info.num_counters,
"num_counters disagreement: query says {num_counters} but function info only has {}",
@ -192,23 +195,23 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
);
bx.instrprof_increment(fn_name, hash, num_counters, index);
}
CoverageKind::ExpressionUsed { id } => {
func_coverage.mark_expression_id_seen(id);
CoverageKind::ExpressionUsed { id: _ } => {
// Expression-used statements are markers that are handled by
// `coverage_ids_info`, so there's nothing to codegen here.
}
CoverageKind::CondBitmapUpdate { index, decision_depth } => {
drop(coverage_map);
let cond_bitmap = bx
.coverage_cx()
let cond_bitmap = coverage_cx
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
.expect("mcdc cond bitmap should have been allocated for updating");
let cond_index = bx.const_i32(index as i32);
bx.mcdc_condbitmap_update(cond_index, cond_bitmap);
}
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
drop(coverage_map);
let cond_bitmap = bx.coverage_cx()
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
.expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
let cond_bitmap =
coverage_cx.try_get_mcdc_condition_bitmap(&instance, decision_depth).expect(
"mcdc cond bitmap should have been allocated for merging \
into the global bitmap",
);
assert!(
bitmap_idx as usize <= function_coverage_info.mcdc_bitmap_bits,
"bitmap index of the decision out of range"

View file

@ -203,6 +203,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&ptr_type_debuginfo_name,
None,
cx.size_and_align_of(ptr_type),
NO_SCOPE_METADATA,
DIFlags::FlagZero,
@ -259,6 +260,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
layout.fields.offset(abi::WIDE_PTR_ADDR),
DIFlags::FlagZero,
data_ptr_type_di_node,
None,
),
build_field_di_node(
cx,
@ -268,6 +270,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
layout.fields.offset(abi::WIDE_PTR_EXTRA),
DIFlags::FlagZero,
type_di_node(cx, extra_field.ty),
None,
),
]
},
@ -369,6 +372,7 @@ fn build_dyn_type_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&type_name,
None,
cx.size_and_align_of(dyn_type),
NO_SCOPE_METADATA,
DIFlags::FlagZero,
@ -722,6 +726,14 @@ fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreation
// `f16`'s value to be displayed using a Natvis visualiser in `intrinsic.natvis`.
let float_ty = cx.tcx.types.f16;
let bits_ty = cx.tcx.types.u16;
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
match float_ty.kind() {
ty::Adt(def, _) => Some(file_metadata_from_def_id(cx, Some(def.did()))),
_ => None,
}
} else {
None
};
type_map::build_type_with_children(
cx,
type_map::stub(
@ -729,12 +741,21 @@ fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreation
Stub::Struct,
UniqueTypeId::for_ty(cx.tcx, float_ty),
"f16",
def_location,
cx.size_and_align_of(float_ty),
NO_SCOPE_METADATA,
DIFlags::FlagZero,
),
// Fields:
|cx, float_di_node| {
let def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
match bits_ty.kind() {
ty::Adt(def, _) => Some(def.did()),
_ => None,
}
} else {
None
};
smallvec![build_field_di_node(
cx,
float_di_node,
@ -743,6 +764,7 @@ fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreation
Size::ZERO,
DIFlags::FlagZero,
type_di_node(cx, bits_ty),
def_id,
)]
},
NO_GENERICS,
@ -839,6 +861,7 @@ fn build_foreign_type_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&compute_debuginfo_type_name(cx.tcx, t, false),
None,
cx.size_and_align_of(t),
Some(get_namespace_for_item(cx, def_id)),
DIFlags::FlagZero,
@ -989,15 +1012,22 @@ fn build_field_di_node<'ll, 'tcx>(
offset: Size,
flags: DIFlags,
type_di_node: &'ll DIType,
def_id: Option<DefId>,
) -> &'ll DIType {
let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers
{
file_metadata_from_def_id(cx, def_id)
} else {
(unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
};
unsafe {
llvm::LLVMRustDIBuilderCreateMemberType(
DIB(cx),
owner,
name.as_c_char_ptr(),
name.len(),
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
file_metadata,
line_number,
size_and_align.0.bits(),
size_and_align.1.bits() as u32,
offset.bits(),
@ -1041,6 +1071,11 @@ fn build_struct_type_di_node<'ll, 'tcx>(
let containing_scope = get_namespace_for_item(cx, adt_def.did());
let struct_type_and_layout = cx.layout_of(struct_type);
let variant_def = adt_def.non_enum_variant();
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(adt_def.did())))
} else {
None
};
type_map::build_type_with_children(
cx,
@ -1049,6 +1084,7 @@ fn build_struct_type_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&compute_debuginfo_type_name(cx.tcx, struct_type, false),
def_location,
size_and_align_of(struct_type_and_layout),
Some(containing_scope),
visibility_di_flags(cx, adt_def.did(), adt_def.did()),
@ -1068,6 +1104,11 @@ fn build_struct_type_di_node<'ll, 'tcx>(
Cow::Borrowed(f.name.as_str())
};
let field_layout = struct_type_and_layout.field(cx, i);
let def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(f.did)
} else {
None
};
build_field_di_node(
cx,
owner,
@ -1076,6 +1117,7 @@ fn build_struct_type_di_node<'ll, 'tcx>(
struct_type_and_layout.fields.offset(i),
visibility_di_flags(cx, f.did, adt_def.did()),
type_di_node(cx, field_layout.ty),
def_id,
)
})
.collect()
@ -1125,6 +1167,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>(
layout.fields.offset(index),
DIFlags::FlagZero,
type_di_node(cx, up_var_ty),
None,
)
})
.collect()
@ -1150,6 +1193,7 @@ fn build_tuple_type_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&type_name,
None,
size_and_align_of(tuple_type_and_layout),
NO_SCOPE_METADATA,
DIFlags::FlagZero,
@ -1168,6 +1212,7 @@ fn build_tuple_type_di_node<'ll, 'tcx>(
tuple_type_and_layout.fields.offset(index),
DIFlags::FlagZero,
type_di_node(cx, component_type),
None,
)
})
.collect()
@ -1189,6 +1234,12 @@ fn build_closure_env_di_node<'ll, 'tcx>(
let containing_scope = get_namespace_for_item(cx, def_id);
let type_name = compute_debuginfo_type_name(cx.tcx, closure_env_type, false);
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(def_id)))
} else {
None
};
type_map::build_type_with_children(
cx,
type_map::stub(
@ -1196,6 +1247,7 @@ fn build_closure_env_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&type_name,
def_location,
cx.size_and_align_of(closure_env_type),
Some(containing_scope),
DIFlags::FlagZero,
@ -1219,6 +1271,11 @@ fn build_union_type_di_node<'ll, 'tcx>(
let containing_scope = get_namespace_for_item(cx, union_def_id);
let union_ty_and_layout = cx.layout_of(union_type);
let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false);
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(union_def_id)))
} else {
None
};
type_map::build_type_with_children(
cx,
@ -1227,6 +1284,7 @@ fn build_union_type_di_node<'ll, 'tcx>(
Stub::Union,
unique_type_id,
&type_name,
def_location,
size_and_align_of(union_ty_and_layout),
Some(containing_scope),
DIFlags::FlagZero,
@ -1239,6 +1297,11 @@ fn build_union_type_di_node<'ll, 'tcx>(
.enumerate()
.map(|(i, f)| {
let field_layout = union_ty_and_layout.field(cx, i);
let def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(f.did)
} else {
None
};
build_field_di_node(
cx,
owner,
@ -1247,6 +1310,7 @@ fn build_union_type_di_node<'ll, 'tcx>(
Size::ZERO,
DIFlags::FlagZero,
type_di_node(cx, field_layout.ty),
def_id,
)
})
.collect()
@ -1321,14 +1385,7 @@ pub(crate) fn build_global_var_di_node<'ll>(
// We may want to remove the namespace scope if we're in an extern block (see
// https://github.com/rust-lang/rust/pull/46457#issuecomment-351750952).
let var_scope = get_namespace_for_item(cx, def_id);
let span = hygiene::walk_chain_collapsed(tcx.def_span(def_id), DUMMY_SP);
let (file_metadata, line_number) = if !span.is_dummy() {
let loc = cx.lookup_debug_loc(span.lo());
(file_metadata(cx, &loc.file), loc.line)
} else {
(unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
};
let (file_metadata, line_number) = file_metadata_from_def_id(cx, Some(def_id));
let is_local_to_unit = is_node_local_to_unit(cx, def_id);
@ -1418,6 +1475,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
Stub::VTableTy { vtable_holder },
unique_type_id,
&vtable_type_name,
None,
(size, pointer_align),
NO_SCOPE_METADATA,
DIFlags::FlagArtificial,
@ -1455,6 +1513,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
field_offset,
DIFlags::FlagZero,
field_type_di_node,
None,
))
})
.collect()
@ -1606,3 +1665,20 @@ fn tuple_field_name(field_index: usize) -> Cow<'static, str> {
.map(|s| Cow::from(*s))
.unwrap_or_else(|| Cow::from(format!("__{field_index}")))
}
pub(crate) type DefinitionLocation<'ll> = (&'ll DIFile, c_uint);
pub(crate) fn file_metadata_from_def_id<'ll>(
cx: &CodegenCx<'ll, '_>,
def_id: Option<DefId>,
) -> DefinitionLocation<'ll> {
if let Some(def_id) = def_id
&& let span = hygiene::walk_chain_collapsed(cx.tcx.def_span(def_id), DUMMY_SP)
&& !span.is_dummy()
{
let loc = cx.lookup_debug_loc(span.lo());
(file_metadata(cx, &loc.file), loc.line)
} else {
(unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
}
}

View file

@ -4,7 +4,7 @@ use libc::c_uint;
use rustc_abi::{Align, Endian, Size, TagEncoding, VariantIdx, Variants};
use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name;
use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
use rustc_codegen_ssa::traits::ConstCodegenMethods;
use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods};
use rustc_index::IndexVec;
use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
@ -16,8 +16,8 @@ use crate::debuginfo::metadata::enums::DiscrResult;
use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId};
use crate::debuginfo::metadata::{
DINodeCreationResult, NO_GENERICS, NO_SCOPE_METADATA, SmallVec, UNKNOWN_LINE_NUMBER,
build_field_di_node, file_metadata, size_and_align_of, type_di_node, unknown_file_metadata,
visibility_di_flags,
build_field_di_node, file_metadata, file_metadata_from_def_id, size_and_align_of, type_di_node,
unknown_file_metadata, visibility_di_flags,
};
use crate::debuginfo::utils::DIB;
use crate::llvm::debuginfo::{DIFile, DIFlags, DIType};
@ -192,6 +192,12 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
assert!(!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout));
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(enum_adt_def.did())))
} else {
None
};
type_map::build_type_with_children(
cx,
type_map::stub(
@ -199,6 +205,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
type_map::Stub::Union,
unique_type_id,
&enum_type_name,
def_location,
cx.size_and_align_of(enum_type),
NO_SCOPE_METADATA,
visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did()),
@ -262,6 +269,14 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
unique_type_id: UniqueTypeId<'tcx>,
) -> DINodeCreationResult<'ll> {
let coroutine_type = unique_type_id.expect_ty();
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
let &ty::Coroutine(coroutine_def_id, _) = coroutine_type.kind() else {
bug!("build_coroutine_di_node() called with non-coroutine type: `{:?}`", coroutine_type)
};
Some(file_metadata_from_def_id(cx, Some(coroutine_def_id)))
} else {
None
};
let coroutine_type_and_layout = cx.layout_of(coroutine_type);
let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false);
@ -274,6 +289,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
type_map::Stub::Union,
unique_type_id,
&coroutine_type_name,
def_location,
size_and_align_of(coroutine_type_and_layout),
NO_SCOPE_METADATA,
DIFlags::FlagZero,
@ -321,6 +337,12 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
let tag_base_type_di_node = type_di_node(cx, tag_base_type);
let tag_base_type_align = cx.align_of(tag_base_type);
let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(enum_adt_def.did())
} else {
None
};
let variant_names_type_di_node = build_variant_names_type_di_node(
cx,
enum_type_di_node,
@ -328,6 +350,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
variant_index,
Cow::from(enum_adt_def.variant(variant_index).name.as_str()),
)),
enum_adt_def_id,
);
let variant_struct_type_wrapper_di_node = build_variant_struct_wrapper_type_di_node(
@ -341,6 +364,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
tag_base_type_di_node,
tag_base_type,
DiscrResult::NoDiscriminant,
None,
);
smallvec![
@ -354,6 +378,7 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
Size::ZERO,
visibility_flags,
variant_struct_type_wrapper_di_node,
None,
),
unsafe {
llvm::LLVMRustDIBuilderCreateStaticMemberType(
@ -383,6 +408,12 @@ fn build_union_fields_for_enum<'ll, 'tcx>(
) -> SmallVec<&'ll DIType> {
let tag_base_type = tag_base_type(cx.tcx, enum_type_and_layout);
let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(enum_adt_def.did())
} else {
None
};
let variant_names_type_di_node = build_variant_names_type_di_node(
cx,
enum_type_di_node,
@ -390,6 +421,7 @@ fn build_union_fields_for_enum<'ll, 'tcx>(
let variant_name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
(variant_index, variant_name)
}),
enum_adt_def_id,
);
let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did());
@ -447,6 +479,7 @@ fn build_variant_names_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
containing_scope: &'ll DIType,
variants: impl Iterator<Item = (VariantIdx, Cow<'tcx, str>)>,
enum_def_id: Option<rustc_span::def_id::DefId>,
) -> &'ll DIType {
// Create an enumerator for each variant.
super::build_enumeration_type_di_node(
@ -454,6 +487,7 @@ fn build_variant_names_type_di_node<'ll, 'tcx>(
"VariantNames",
variant_names_enum_base_type(cx),
variants.map(|(variant_index, variant_name)| (variant_name, variant_index.as_u32().into())),
enum_def_id,
containing_scope,
)
}
@ -469,6 +503,7 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>(
tag_base_type_di_node: &'ll DIType,
tag_base_type: Ty<'tcx>,
discr: DiscrResult,
source_info: Option<(&'ll DIFile, c_uint)>,
) -> &'ll DIType {
type_map::build_type_with_children(
cx,
@ -481,6 +516,7 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>(
variant_index,
),
&variant_struct_wrapper_type_name(variant_index),
source_info,
// NOTE: We use size and align of enum_type, not from variant_layout:
size_and_align_of(enum_or_coroutine_type_and_layout),
Some(enum_or_coroutine_type_di_node),
@ -530,6 +566,7 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>(
Size::ZERO,
DIFlags::FlagZero,
variant_struct_type_di_node,
None,
));
let build_assoc_const =
@ -684,6 +721,11 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>(
variant_range
.clone()
.map(|variant_index| (variant_index, CoroutineArgs::variant_name(variant_index))),
if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(coroutine_def_id)
} else {
None
},
);
let discriminants: IndexVec<VariantIdx, DiscrResult> = {
@ -776,6 +818,11 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
tag_base_type_di_node,
tag_base_type,
variant_member_info.discr,
if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
variant_member_info.source_info
} else {
None
},
);
// We use LLVMRustDIBuilderCreateMemberType() member type directly because
@ -831,6 +878,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
lo_offset,
di_flags,
type_di_node,
None,
));
unions_fields.push(build_field_di_node(
@ -841,6 +889,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
hi_offset,
DIFlags::FlagZero,
type_di_node,
None,
));
} else {
unions_fields.push(build_field_di_node(
@ -851,6 +900,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
enum_type_and_layout.fields.offset(tag_field),
di_flags,
tag_base_type_di_node,
None,
));
}

View file

@ -3,6 +3,7 @@ use std::borrow::Cow;
use rustc_abi::{FieldIdx, TagEncoding, VariantIdx, Variants};
use rustc_codegen_ssa::debuginfo::type_names::{compute_debuginfo_type_name, cpp_like_debuginfo};
use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
use rustc_codegen_ssa::traits::MiscCodegenMethods;
use rustc_hir::def::CtorKind;
use rustc_index::IndexSlice;
use rustc_middle::bug;
@ -16,8 +17,8 @@ use super::{SmallVec, size_and_align_of};
use crate::common::{AsCCharPtr, CodegenCx};
use crate::debuginfo::metadata::type_map::{self, Stub};
use crate::debuginfo::metadata::{
UNKNOWN_LINE_NUMBER, build_field_di_node, build_generic_type_param_di_nodes, type_di_node,
unknown_file_metadata,
UNKNOWN_LINE_NUMBER, build_field_di_node, build_generic_type_param_di_nodes,
file_metadata_from_def_id, type_di_node, unknown_file_metadata,
};
use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item};
use crate::llvm::debuginfo::{DIFlags, DIType};
@ -68,6 +69,11 @@ fn build_c_style_enum_di_node<'ll, 'tcx>(
enum_type_and_layout: TyAndLayout<'tcx>,
) -> DINodeCreationResult<'ll> {
let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(enum_adt_def.did())
} else {
None
};
DINodeCreationResult {
di_node: build_enumeration_type_di_node(
cx,
@ -77,6 +83,7 @@ fn build_c_style_enum_di_node<'ll, 'tcx>(
let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
(name, discr.val)
}),
enum_adt_def_id,
containing_scope,
),
already_stored_in_typemap: false,
@ -92,6 +99,7 @@ fn build_enumeration_type_di_node<'ll, 'tcx>(
type_name: &str,
base_type: Ty<'tcx>,
enumerators: impl Iterator<Item = (Cow<'tcx, str>, u128)>,
def_id: Option<rustc_span::def_id::DefId>,
containing_scope: &'ll DIType,
) -> &'ll DIType {
let is_unsigned = match base_type.kind() {
@ -115,14 +123,21 @@ fn build_enumeration_type_di_node<'ll, 'tcx>(
})
.collect();
let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers
{
file_metadata_from_def_id(cx, def_id)
} else {
(unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
};
unsafe {
llvm::LLVMRustDIBuilderCreateEnumerationType(
DIB(cx),
containing_scope,
type_name.as_c_char_ptr(),
type_name.len(),
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
file_metadata,
line_number,
size.bits(),
align.bits() as u32,
create_DIArray(DIB(cx), &enumerator_di_nodes[..]),
@ -193,6 +208,12 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
) -> &'ll DIType {
assert_eq!(variant_layout.ty, enum_type_and_layout.ty);
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(variant_def.def_id)))
} else {
None
};
type_map::build_type_with_children(
cx,
type_map::stub(
@ -204,6 +225,7 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
variant_index,
),
variant_def.name.as_str(),
def_location,
// NOTE: We use size and align of enum_type, not from variant_layout:
size_and_align_of(enum_type_and_layout),
Some(enum_type_di_node),
@ -231,6 +253,7 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
variant_layout.fields.offset(field_index),
di_flags,
type_di_node(cx, field_layout.ty),
None,
)
})
.collect::<SmallVec<_>>()
@ -286,6 +309,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&variant_name,
None,
size_and_align_of(coroutine_type_and_layout),
Some(coroutine_type_di_node),
DIFlags::FlagZero,
@ -312,6 +336,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
variant_layout.fields.offset(field_index),
DIFlags::FlagZero,
type_di_node(cx, field_type),
None,
)
})
.collect();
@ -331,6 +356,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
coroutine_type_and_layout.fields.offset(index),
DIFlags::FlagZero,
type_di_node(cx, upvar_ty),
None,
)
})
.collect();

View file

@ -4,7 +4,7 @@ use libc::c_uint;
use rustc_abi::{Size, TagEncoding, VariantIdx, Variants};
use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name;
use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
use rustc_codegen_ssa::traits::ConstCodegenMethods;
use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods};
use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self};
@ -14,7 +14,8 @@ use crate::common::{AsCCharPtr, CodegenCx};
use crate::debuginfo::metadata::type_map::{self, Stub, StubInfo, UniqueTypeId};
use crate::debuginfo::metadata::{
DINodeCreationResult, NO_GENERICS, SmallVec, UNKNOWN_LINE_NUMBER, file_metadata,
size_and_align_of, type_di_node, unknown_file_metadata, visibility_di_flags,
file_metadata_from_def_id, size_and_align_of, type_di_node, unknown_file_metadata,
visibility_di_flags,
};
use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item};
use crate::llvm::debuginfo::{DIFile, DIFlags, DIType};
@ -55,6 +56,12 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
assert!(!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout));
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(enum_adt_def.did())))
} else {
None
};
type_map::build_type_with_children(
cx,
type_map::stub(
@ -62,6 +69,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&enum_type_name,
def_location,
size_and_align_of(enum_type_and_layout),
Some(containing_scope),
visibility_flags,
@ -84,14 +92,27 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
enum_type_and_layout.for_variant(cx, variant_index),
visibility_flags,
),
source_info: None,
source_info: if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(
cx,
Some(enum_adt_def.variant(variant_index).def_id),
))
} else {
None
},
})
.collect();
let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(enum_adt_def.did())
} else {
None
};
smallvec![build_enum_variant_part_di_node(
cx,
enum_type_and_layout,
enum_type_di_node,
enum_adt_def_id,
&variant_member_infos[..],
)]
},
@ -134,6 +155,12 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false);
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(coroutine_def_id)))
} else {
None
};
type_map::build_type_with_children(
cx,
type_map::stub(
@ -141,6 +168,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
Stub::Struct,
unique_type_id,
&coroutine_type_name,
def_location,
size_and_align_of(coroutine_type_and_layout),
Some(containing_scope),
DIFlags::FlagZero,
@ -197,10 +225,16 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
})
.collect();
let coroutine_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(coroutine_def_id)
} else {
None
};
smallvec![build_enum_variant_part_di_node(
cx,
coroutine_type_and_layout,
coroutine_type_di_node,
coroutine_def_id,
&variant_struct_type_di_nodes[..],
)]
},
@ -228,6 +262,7 @@ fn build_enum_variant_part_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
enum_type_and_layout: TyAndLayout<'tcx>,
enum_type_di_node: &'ll DIType,
enum_type_def_id: Option<rustc_span::def_id::DefId>,
variant_member_infos: &[VariantMemberInfo<'_, 'll>],
) -> &'ll DIType {
let tag_member_di_node =
@ -236,6 +271,13 @@ fn build_enum_variant_part_di_node<'ll, 'tcx>(
let variant_part_unique_type_id =
UniqueTypeId::for_enum_variant_part(cx.tcx, enum_type_and_layout.ty);
let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers
{
file_metadata_from_def_id(cx, enum_type_def_id)
} else {
(unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
};
let stub = StubInfo::new(
cx,
variant_part_unique_type_id,
@ -246,8 +288,8 @@ fn build_enum_variant_part_di_node<'ll, 'tcx>(
enum_type_di_node,
variant_part_name.as_c_char_ptr(),
variant_part_name.len(),
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
file_metadata,
line_number,
enum_type_and_layout.size.bits(),
enum_type_and_layout.align.abi.bits() as u32,
DIFlags::FlagZero,

View file

@ -8,7 +8,7 @@ use rustc_macros::HashStable;
use rustc_middle::bug;
use rustc_middle::ty::{self, PolyExistentialTraitRef, Ty, TyCtxt};
use super::{SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata};
use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata};
use crate::common::{AsCCharPtr, CodegenCx};
use crate::debuginfo::utils::{DIB, create_DIArray, debug_context};
use crate::llvm::debuginfo::{DIFlags, DIScope, DIType};
@ -186,6 +186,7 @@ pub(super) fn stub<'ll, 'tcx>(
kind: Stub<'ll>,
unique_type_id: UniqueTypeId<'tcx>,
name: &str,
def_location: Option<DefinitionLocation<'ll>>,
(size, align): (Size, Align),
containing_scope: Option<&'ll DIScope>,
flags: DIFlags,
@ -193,6 +194,12 @@ pub(super) fn stub<'ll, 'tcx>(
let empty_array = create_DIArray(DIB(cx), &[]);
let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
let (file_metadata, line_number) = if let Some(def_location) = def_location {
(def_location.0, def_location.1)
} else {
(unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
};
let metadata = match kind {
Stub::Struct | Stub::VTableTy { .. } => {
let vtable_holder = match kind {
@ -205,8 +212,8 @@ pub(super) fn stub<'ll, 'tcx>(
containing_scope,
name.as_c_char_ptr(),
name.len(),
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
file_metadata,
line_number,
size.bits(),
align.bits() as u32,
flags,
@ -225,8 +232,8 @@ pub(super) fn stub<'ll, 'tcx>(
containing_scope,
name.as_c_char_ptr(),
name.len(),
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
file_metadata,
line_number,
size.bits(),
align.bits() as u32,
flags,

View file

@ -1534,6 +1534,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)),
sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
sym::simd_relaxed_fma => ("fmuladd", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)),
sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)),
sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
@ -1572,6 +1573,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
| sym::simd_fpowi
| sym::simd_fsin
| sym::simd_fsqrt
| sym::simd_relaxed_fma
| sym::simd_round
| sym::simd_trunc
) {

View file

@ -17,7 +17,6 @@
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end

View file

@ -2240,6 +2240,7 @@ unsafe extern "C" {
Output: *const c_char,
DwoOutput: *const c_char,
FileType: FileType,
VerifyIR: bool,
) -> LLVMRustResult;
pub fn LLVMRustOptimize<'a>(
M: &'a Module,

View file

@ -97,7 +97,7 @@ fn minimum_deployment_target(target: &Target) -> OSVersion {
}
/// Name of the environment variable used to fetch the deployment target on the given OS.
fn deployment_target_env_var(os: &str) -> &'static str {
pub fn deployment_target_env_var(os: &str) -> &'static str {
match os {
"macos" => "MACOSX_DEPLOYMENT_TARGET",
"ios" => "IPHONEOS_DEPLOYMENT_TARGET",

View file

@ -1386,7 +1386,7 @@ fn link_sanitizer_runtime(
let filename = format!("rustc{channel}_rt.{name}");
let path = find_sanitizer_runtime(sess, &filename);
let rpath = path.to_str().expect("non-utf8 component in path");
linker.cc_args(&["-Wl,-rpath", "-Xlinker", rpath]);
linker.link_args(&["-rpath", rpath]);
linker.link_dylib_by_name(&filename, false, true);
} else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" {
// MSVC provides the `/INFERASANLIBS` argument to automatically find the
@ -2210,7 +2210,7 @@ fn add_rpath_args(
is_like_osx: sess.target.is_like_osx,
linker_is_gnu: sess.target.linker_flavor.is_gnu(),
};
cmd.cc_args(&rpath::get_rpath_flags(&rpath_config));
cmd.link_args(&rpath::get_rpath_linker_args(&rpath_config));
}
}

View file

@ -24,6 +24,9 @@ use super::command::Command;
use super::symbol_export;
use crate::errors;
#[cfg(test)]
mod tests;
/// Disables non-English messages from localized linkers.
/// Such messages may cause issues with text encoding on Windows (#35785)
/// and prevent inspection of linker output in case of errors, which we occasionally do.
@ -178,23 +181,42 @@ fn verbatim_args<L: Linker + ?Sized>(
}
l
}
/// Arguments for the underlying linker.
/// Add options to pass them through cc wrapper if `Linker` is a cc wrapper.
fn link_args<L: Linker + ?Sized>(
l: &mut L,
args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>,
) -> &mut L {
let args = args.into_iter();
if !l.is_cc() {
verbatim_args(l, args);
} else if args.len() != 0 {
// FIXME: Support arguments with commas, see `rpaths_to_flags` for the example.
let mut combined_arg = OsString::from("-Wl");
for arg in args {
/// Add underlying linker arguments to C compiler command, by wrapping them in
/// `-Wl` or `-Xlinker`.
fn convert_link_args_to_cc_args(cmd: &mut Command, args: impl IntoIterator<Item: AsRef<OsStr>>) {
let mut combined_arg = OsString::from("-Wl");
for arg in args {
// If the argument itself contains a comma, we need to emit it
// as `-Xlinker`, otherwise we can use `-Wl`.
if arg.as_ref().as_encoded_bytes().contains(&b',') {
// Emit current `-Wl` argument, if any has been built.
if combined_arg != OsStr::new("-Wl") {
cmd.arg(combined_arg);
// Begin next `-Wl` argument.
combined_arg = OsString::from("-Wl");
}
// Emit `-Xlinker` argument.
cmd.arg("-Xlinker");
cmd.arg(arg);
} else {
// Append to `-Wl` argument.
combined_arg.push(",");
combined_arg.push(arg);
}
l.cmd().arg(combined_arg);
}
// Emit final `-Wl` argument.
if combined_arg != OsStr::new("-Wl") {
cmd.arg(combined_arg);
}
}
/// Arguments for the underlying linker.
/// Add options to pass them through cc wrapper if `Linker` is a cc wrapper.
fn link_args<L: Linker + ?Sized>(l: &mut L, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut L {
if !l.is_cc() {
verbatim_args(l, args);
} else {
convert_link_args_to_cc_args(l.cmd(), args);
}
l
}
@ -224,7 +246,7 @@ macro_rules! generate_arg_methods {
verbatim_args(self, iter::once(arg))
}
#[allow(unused)]
pub(crate) fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>) -> &mut Self {
pub(crate) fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {
link_args(self, args)
}
#[allow(unused)]

View file

@ -0,0 +1,32 @@
use super::*;
#[test]
fn test_rpaths_to_args() {
let mut cmd = Command::new("foo");
convert_link_args_to_cc_args(&mut cmd, &["-rpath", "path1", "-rpath", "path2"]);
assert_eq!(cmd.get_args(), [OsStr::new("-Wl,-rpath,path1,-rpath,path2")]);
}
#[test]
fn test_xlinker() {
let mut cmd = Command::new("foo");
convert_link_args_to_cc_args(&mut cmd, &[
"arg1",
"arg2",
"arg3,with,comma",
"arg4,with,comma",
"arg5",
"arg6,with,comma",
]);
assert_eq!(cmd.get_args(), [
OsStr::new("-Wl,arg1,arg2"),
OsStr::new("-Xlinker"),
OsStr::new("arg3,with,comma"),
OsStr::new("-Xlinker"),
OsStr::new("arg4,with,comma"),
OsStr::new("-Wl,arg5"),
OsStr::new("-Xlinker"),
OsStr::new("arg6,with,comma"),
]);
}

View file

@ -13,39 +13,27 @@ pub(super) struct RPathConfig<'a> {
pub linker_is_gnu: bool,
}
pub(super) fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec<OsString> {
pub(super) fn get_rpath_linker_args(config: &RPathConfig<'_>) -> Vec<OsString> {
debug!("preparing the RPATH!");
let rpaths = get_rpaths(config);
let mut flags = rpaths_to_flags(rpaths);
let mut args = Vec::with_capacity(rpaths.len() * 2); // the minimum needed capacity
for rpath in rpaths {
args.push("-rpath".into());
args.push(rpath);
}
if config.linker_is_gnu {
// Use DT_RUNPATH instead of DT_RPATH if available
flags.push("-Wl,--enable-new-dtags".into());
args.push("--enable-new-dtags".into());
// Set DF_ORIGIN for substitute $ORIGIN
flags.push("-Wl,-z,origin".into());
args.push("-z".into());
args.push("origin".into());
}
flags
}
fn rpaths_to_flags(rpaths: Vec<OsString>) -> Vec<OsString> {
let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity
for rpath in rpaths {
if rpath.to_string_lossy().contains(',') {
ret.push("-Wl,-rpath".into());
ret.push("-Xlinker".into());
ret.push(rpath);
} else {
let mut single_arg = OsString::from("-Wl,-rpath,");
single_arg.push(rpath);
ret.push(single_arg);
}
}
ret
args
}
fn get_rpaths(config: &RPathConfig<'_>) -> Vec<OsString> {

View file

@ -1,13 +1,4 @@
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use super::{RPathConfig, get_rpath_relative_to_output, minimize_rpaths, rpaths_to_flags};
#[test]
fn test_rpaths_to_flags() {
let flags = rpaths_to_flags(vec!["path1".into(), "path2".into()]);
assert_eq!(flags, ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]);
}
use super::*;
#[test]
fn test_minimize1() {
@ -69,15 +60,3 @@ fn test_rpath_relative_issue_119571() {
// Should not panic when lib only contains filename.
let _ = get_rpath_relative_to_output(config, Path::new("libstd.so"));
}
#[test]
fn test_xlinker() {
let args = rpaths_to_flags(vec!["a/normal/path".into(), "a,comma,path".into()]);
assert_eq!(args, vec![
OsString::from("-Wl,-rpath,a/normal/path"),
OsString::from("-Wl,-rpath"),
OsString::from("-Xlinker"),
OsString::from("a,comma,path")
]);
}

View file

@ -282,7 +282,7 @@ fn exported_symbols_provider_local(
}));
}
if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() {
if tcx.local_crate_exports_generics() {
use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility};
use rustc_middle::ty::InstanceKind;
@ -310,6 +310,16 @@ fn exported_symbols_provider_local(
continue;
}
if !tcx.sess.opts.share_generics() {
if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_attr::InlineAttr::Never
{
// this is OK, we explicitly allow sharing inline(never) across crates even
// without share-generics.
} else {
continue;
}
}
match *mono_item {
MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => {
if args.non_erasable_generics().next().is_some() {

View file

@ -1,5 +1,6 @@
use rustc_abi::Primitive::{Int, Pointer};
use rustc_abi::{Align, FieldsShape, Size, TagEncoding, VariantIdx, Variants};
use rustc_abi::{Align, BackendRepr, FieldsShape, Size, TagEncoding, VariantIdx, Variants};
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Ty};
@ -385,15 +386,22 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
if variant_index != untagged_variant {
let niche = self.project_field(bx, tag_field);
let niche_llty = bx.cx().immediate_backend_type(niche.layout);
let BackendRepr::Scalar(scalar) = niche.layout.backend_repr else {
bug!("expected a scalar placeref for the niche");
};
// We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping
// around the `niche`'s type.
// The easiest way to do that is to do wrapping arithmetic on `u128` and then
// masking off any extra bits that occur because we did the arithmetic with too many bits.
let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
let niche_value = (niche_value as u128).wrapping_add(niche_start);
// FIXME(eddyb): check the actual primitive type here.
let niche_llval = if niche_value == 0 {
// HACK(eddyb): using `c_null` as it works on all types.
bx.cx().const_null(niche_llty)
} else {
bx.cx().const_uint_big(niche_llty, niche_value)
};
let niche_value = niche_value & niche.layout.size.unsigned_int_max();
let niche_llval = bx.cx().scalar_to_backend(
Scalar::from_uint(niche_value, niche.layout.size),
scalar,
niche_llty,
);
OperandValue::Immediate(niche_llval).store(bx, niche);
}
}

View file

@ -71,11 +71,11 @@ pub trait CodegenBackend {
need_metadata_module: bool,
) -> Box<dyn Any>;
/// This is called on the returned `Box<dyn Any>` from `codegen_backend`
/// This is called on the returned `Box<dyn Any>` from [`codegen_crate`](Self::codegen_crate)
///
/// # Panics
///
/// Panics when the passed `Box<dyn Any>` was not returned by `codegen_backend`.
/// Panics when the passed `Box<dyn Any>` was not returned by [`codegen_crate`](Self::codegen_crate).
fn join_codegen(
&self,
ongoing_codegen: Box<dyn Any>,
@ -83,7 +83,7 @@ pub trait CodegenBackend {
outputs: &OutputFilenames,
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>);
/// This is called on the returned `CodegenResults` from `join_codegen`
/// This is called on the returned [`CodegenResults`] from [`join_codegen`](Self::join_codegen).
fn link(
&self,
sess: &Session,

View file

@ -110,7 +110,7 @@ const_eval_extern_type_field = `extern type` field does not have a known offset
const_eval_fn_ptr_call =
function pointers need an RFC before allowed to be called in {const_eval_const_context}s
const_eval_for_loop_into_iter_non_const =
cannot convert `{$ty}` into an iterator in {const_eval_const_context}s
cannot use `for` loop on `{$ty}` in {const_eval_const_context}s
const_eval_frame_note = {$times ->
[0] {const_eval_frame_note_inner}
@ -324,11 +324,11 @@ const_eval_ptr_as_bytes_1 =
this code performed an operation that depends on the underlying bytes representing a pointer
const_eval_ptr_as_bytes_2 =
the absolute address of a pointer is not known at compile-time, so such operations are not supported
const_eval_question_branch_non_const =
`?` cannot determine the branch of `{$ty}` in {const_eval_const_context}s
const_eval_question_branch_non_const =
`?` is not allowed on `{$ty}` in {const_eval_const_context}s
const_eval_question_from_residual_non_const =
`?` cannot convert from residual of `{$ty}` in {const_eval_const_context}s
`?` is not allowed on `{$ty}` in {const_eval_const_context}s
const_eval_range = in the range {$lo}..={$hi}
const_eval_range_lower = greater or equal to {$lo}

View file

@ -180,8 +180,10 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
};
}
let mut err = match kind {
CallDesugaringKind::ForLoopIntoIter => {
// Don't point at the trait if this is a desugaring...
// FIXME(const_trait_impl): we could perhaps do this for `Iterator`.
match kind {
CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => {
error!(NonConstForLoopIntoIter)
}
CallDesugaringKind::QuestionBranch => {
@ -196,10 +198,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
CallDesugaringKind::Await => {
error!(NonConstAwait)
}
};
diag_trait(&mut err, self_ty, kind.trait_def_id(tcx));
err
}
}
CallKind::FnCall { fn_trait_id, self_ty } => {
let note = match self_ty.kind() {

View file

@ -139,12 +139,14 @@ where
match error {
// Don't emit a new diagnostic for these errors, they are already reported elsewhere or
// should remain silent.
err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, span),
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
ErrorHandled::TooGeneric(span)
}
err_inval!(AlreadyReported(guar)) => ErrorHandled::Reported(guar, span),
err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
ErrorHandled::Reported(ReportedErrorInfo::tainted_by_errors(guar), span)
// This can occur in infallible promoteds e.g. when a non-existent type or field is
// encountered.
ErrorHandled::Reported(ReportedErrorInfo::allowed_in_infallible(guar), span)
}
// Report remaining errors.
_ => {
@ -152,7 +154,12 @@ where
let span = span.substitute_dummy(our_span);
let err = mk(span, frames);
let mut err = tcx.dcx().create_err(err);
let can_be_spurious = matches!(error, InterpErrorKind::ResourceExhaustion(_));
// We allow invalid programs in infallible promoteds since invalid layouts can occur
// anyway (e.g. due to size overflow). And we allow OOM as that can happen any time.
let allowed_in_infallible = matches!(
error,
InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_)
);
let msg = error.diagnostic_message();
error.add_args(&mut err);
@ -160,8 +167,8 @@ where
// Use *our* span to label the interp error
err.span_label(our_span, msg);
let g = err.emit();
let reported = if can_be_spurious {
ReportedErrorInfo::spurious(g)
let reported = if allowed_in_infallible {
ReportedErrorInfo::allowed_in_infallible(g)
} else {
ReportedErrorInfo::from(g)
};

View file

@ -2,6 +2,7 @@
use rustc_abi::VariantIdx;
use rustc_middle::query::{Key, TyCtxtAt};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, mir};
use tracing::instrument;
@ -85,5 +86,6 @@ pub fn tag_for_variant_provider<'tcx>(
crate::const_eval::DummyMachine,
);
ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag)
let layout = ecx.layout_of(ty).unwrap();
ecx.tag_for_variant(layout, variant_index).unwrap().map(|(tag, _tag_field)| tag)
}

View file

@ -1,7 +1,7 @@
//! Functions for reading and writing discriminants of multi-variant layouts (enums and coroutines).
use rustc_abi::{self as abi, TagEncoding, VariantIdx, Variants};
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
use rustc_middle::ty::{self, CoroutineArgsExt, ScalarInt, Ty};
use rustc_middle::{mir, span_bug};
use tracing::{instrument, trace};
@ -21,17 +21,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
variant_index: VariantIdx,
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
// Layout computation excludes uninhabited variants from consideration
// therefore there's no way to represent those variants in the given layout.
// Essentially, uninhabited variants do not have a tag that corresponds to their
// discriminant, so we cannot do anything here.
// When evaluating we will always error before even getting here, but ConstProp 'executes'
// dead code, so we cannot ICE here.
if dest.layout().for_variant(self, variant_index).is_uninhabited() {
throw_ub!(UninhabitedEnumVariantWritten(variant_index))
}
match self.tag_for_variant(dest.layout().ty, variant_index)? {
match self.tag_for_variant(dest.layout(), variant_index)? {
Some((tag, tag_field)) => {
// No need to validate that the discriminant here because the
// `TyAndLayout::for_variant()` call earlier already checks the
@ -80,7 +70,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if ty.is_enum() {
// Hilariously, `Single` is used even for 0-variant enums.
// (See https://github.com/rust-lang/rust/issues/89765).
if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) {
if ty.ty_adt_def().unwrap().variants().is_empty() {
throw_ub!(UninhabitedEnumVariantRead(index))
}
// For consistency with `write_discriminant`, and to make sure that
@ -188,6 +178,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let variants =
ty.ty_adt_def().expect("tagged layout for non adt").variants();
assert!(variant_index < variants.next_index());
if variant_index == untagged_variant {
// The untagged variant can be in the niche range, but even then it
// is not a valid encoding.
throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size)))
}
variant_index
} else {
untagged_variant
@ -236,10 +231,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// given field index.
pub(crate) fn tag_for_variant(
&self,
ty: Ty<'tcx>,
layout: TyAndLayout<'tcx>,
variant_index: VariantIdx,
) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> {
match self.layout_of(ty)?.variants {
// Layout computation excludes uninhabited variants from consideration.
// Therefore, there's no way to represent those variants in the given layout.
// Essentially, uninhabited variants do not have a tag that corresponds to their
// discriminant, so we have to bail out here.
if layout.for_variant(self, variant_index).is_uninhabited() {
throw_ub!(UninhabitedEnumVariantWritten(variant_index))
}
match layout.variants {
abi::Variants::Single { .. } => {
// The tag of a `Single` enum is like the tag of the niched
// variant: there's no tag as the discriminant is encoded
@ -260,7 +263,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// raw discriminants for enums are isize or bigger during
// their computation, but the in-memory tag is the smallest possible
// representation
let discr = self.discriminant_for_variant(ty, variant_index)?;
let discr = self.discriminant_for_variant(layout.ty, variant_index)?;
let discr_size = discr.layout.size;
let discr_val = discr.to_scalar().to_bits(discr_size)?;
let tag_size = tag_layout.size(self);
@ -286,11 +289,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
..
} => {
assert!(variant_index != untagged_variant);
// We checked that this variant is inhabited, so it must be in the niche range.
assert!(
niche_variants.contains(&variant_index),
"invalid variant index for this enum"
);
let variants_start = niche_variants.start().as_u32();
let variant_index_relative = variant_index
.as_u32()
.checked_sub(variants_start)
.expect("overflow computing relative variant idx");
let variant_index_relative = variant_index.as_u32().strict_sub(variants_start);
// We need to use machine arithmetic when taking into account `niche_start`:
// tag_val = variant_index_relative + niche_start_val
let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;

View file

@ -268,7 +268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
};
// do not continue if typeck errors occurred (can only occur in local crate)
if let Some(err) = body.tainted_by_errors {
throw_inval!(AlreadyReported(ReportedErrorInfo::tainted_by_errors(err)));
throw_inval!(AlreadyReported(ReportedErrorInfo::from(err)));
}
interp_ok(body)
}
@ -585,13 +585,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
match err {
ErrorHandled::TooGeneric(..) => {},
ErrorHandled::Reported(reported, span) => {
if reported.is_tainted_by_errors() {
// const-eval will return "tainted" errors if e.g. the layout cannot
// be computed as the type references non-existing names.
// See <https://github.com/rust-lang/rust/issues/124348>.
} else if reported.can_be_spurious() {
if reported.is_allowed_in_infallible() {
// These errors can just sometimes happen, even when the expression
// is nominally "infallible", e.g. when running out of memory.
// is nominally "infallible", e.g. when running out of memory
// or when some layout could not be computed.
} else {
// Looks like the const is not captured by `required_consts`, that's bad.
span_bug!(span, "interpret const eval failure of {val:?} which is not in required_consts");

View file

@ -10,6 +10,7 @@
#![feature(never_type)]
#![feature(rustdoc_internals)]
#![feature(slice_ptr_get)]
#![feature(strict_overflow_ops)]
#![feature(trait_alias)]
#![feature(try_blocks)]
#![feature(unqualified_local_imports)]

View file

@ -415,6 +415,10 @@ impl<O: ForestObligation> ObligationForest<O> {
.collect()
}
pub fn has_pending_obligations(&self) -> bool {
self.nodes.iter().any(|node| node.state.get() == NodeState::Pending)
}
fn insert_into_error_cache(&mut self, index: usize) {
let node = &self.nodes[index];
self.error_cache

View file

@ -418,7 +418,9 @@ fn run_compiler(
return early_exit();
}
if sess.opts.unstable_opts.parse_only || sess.opts.unstable_opts.show_span.is_some() {
if sess.opts.unstable_opts.parse_crate_root_only
|| sess.opts.unstable_opts.show_span.is_some()
{
return early_exit();
}
@ -865,8 +867,9 @@ fn print_crate_info(
DeploymentTarget => {
if sess.target.is_like_osx {
println_info!(
"deployment_target={}",
apple::pretty_version(apple::deployment_target(sess))
"{}={}",
apple::deployment_target_env_var(&sess.target.os),
apple::pretty_version(apple::deployment_target(sess)),
)
} else {
#[allow(rustc::diagnostic_outside_of_impl)]

View file

@ -15,7 +15,7 @@ unsafe { printf(); } // error!
Using this declaration, it must be called with at least one argument, so
simply calling `printf()` is invalid. But the following uses are allowed:
```
```rust,edition2021
# use std::os::raw::{c_char, c_int};
# #[cfg_attr(all(windows, target_env = "msvc"),
# link(name = "legacy_stdio_definitions",
@ -23,16 +23,11 @@ simply calling `printf()` is invalid. But the following uses are allowed:
# extern "C" { fn printf(_: *const c_char, ...) -> c_int; }
# fn main() {
unsafe {
use std::ffi::CString;
printf(c"test\n".as_ptr());
let fmt = CString::new("test\n").unwrap();
printf(fmt.as_ptr());
printf(c"number = %d\n".as_ptr(), 3);
let fmt = CString::new("number = %d\n").unwrap();
printf(fmt.as_ptr(), 3);
let fmt = CString::new("%d, %d\n").unwrap();
printf(fmt.as_ptr(), 10, 5);
printf(c"%d, %d\n".as_ptr(), 10, 5);
}
# }
```

View file

@ -7,10 +7,11 @@ Erroneous code example:
#![allow(internal_features)]
extern "rust-intrinsic" {
pub static breakpoint: fn(); // error: intrinsic must be a function
pub static atomic_singlethreadfence_seqcst: fn();
// error: intrinsic must be a function
}
fn main() { unsafe { breakpoint(); } }
fn main() { unsafe { atomic_singlethreadfence_seqcst(); } }
```
An intrinsic is a function available for use in a given programming language
@ -22,8 +23,8 @@ error, just declare a function. Example:
#![allow(internal_features)]
extern "rust-intrinsic" {
pub fn breakpoint(); // ok!
pub fn atomic_singlethreadfence_seqcst(); // ok!
}
fn main() { unsafe { breakpoint(); } }
fn main() { unsafe { atomic_singlethreadfence_seqcst(); } }
```

View file

@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
use std::process::ExitStatus;
use rustc_abi::TargetDataLayoutErrors;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast_pretty::pprust;
use rustc_macros::Subdiagnostic;
use rustc_span::Span;
@ -298,6 +299,12 @@ impl IntoDiagArg for hir::def::Namespace {
}
}
impl IntoDiagArg for ExprPrecedence {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Number(self as i32)
}
}
#[derive(Clone)]
pub struct DiagSymbolList<S = Symbol>(Vec<S>);

View file

@ -732,7 +732,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
_ => item.to_tokens(),
};
let attr_item = attr.unwrap_normal_item();
if let AttrArgs::Eq(..) = attr_item.args {
if let AttrArgs::Eq { .. } = attr_item.args {
self.cx.dcx().emit_err(UnsupportedKeyValue { span });
}
let inner_tokens = attr_item.args.inner_tokens();

View file

@ -193,6 +193,9 @@ declare_features! (
(accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)),
/// Allows arbitrary expressions in key-value attributes at parse time.
(accepted, extended_key_value_attributes, "1.54.0", Some(78835)),
/// Allows using `efiapi`, `aapcs`, `sysv64` and `win64` as calling
/// convention for functions with varargs.
(accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)),
/// Allows resolving absolute paths as paths from other crates.
(accepted, extern_absolute_paths, "1.30.0", Some(44660)),
/// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude.
@ -225,7 +228,7 @@ declare_features! (
/// Allows the use of `if let` expressions.
(accepted, if_let, "1.0.0", None),
/// Rescoping temporaries in `if let` to align with Rust 2024.
(accepted, if_let_rescope, "CURRENT_RUSTC_VERSION", Some(124085)),
(accepted, if_let_rescope, "1.84.0", Some(124085)),
/// Allows top level or-patterns (`p | q`) in `if let` and `while let`.
(accepted, if_while_or_patterns, "1.33.0", Some(48215)),
/// Allows lifetime elision in `impl` headers. For example:
@ -357,7 +360,7 @@ declare_features! (
(accepted, repr_transparent, "1.28.0", Some(43036)),
/// Allows enums like Result<T, E> to be used across FFI, if T's niche value can
/// be used to describe E or vice-versa.
(accepted, result_ffi_guarantees, "CURRENT_RUSTC_VERSION", Some(110503)),
(accepted, result_ffi_guarantees, "1.84.0", Some(110503)),
/// Allows return-position `impl Trait` in traits.
(accepted, return_position_impl_trait_in_trait, "1.75.0", Some(91611)),
/// Allows code like `let x: &'static u32 = &42` to work (RFC 1414).
@ -367,7 +370,7 @@ declare_features! (
/// Allows `Self` struct constructor (RFC 2302).
(accepted, self_struct_ctor, "1.32.0", Some(51994)),
/// Shortern the tail expression lifetime
(accepted, shorter_tail_lifetimes, "CURRENT_RUSTC_VERSION", Some(123739)),
(accepted, shorter_tail_lifetimes, "1.84.0", Some(123739)),
/// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`.
(accepted, slice_patterns, "1.42.0", Some(62254)),
/// Allows use of `&foo[a..b]` as a slicing syntax.

View file

@ -101,7 +101,7 @@ declare_features! (
/// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238).
(removed, dropck_parametricity, "1.38.0", Some(28498), None),
/// Uses generic effect parameters for ~const bounds
(removed, effects, "CURRENT_RUSTC_VERSION", Some(102090),
(removed, effects, "1.84.0", Some(102090),
Some("removed, redundant with `#![feature(const_trait_impl)]`")),
/// Allows defining `existential type`s.
(removed, existential_type, "1.38.0", Some(63063),
@ -119,6 +119,13 @@ declare_features! (
(removed, generator_clone, "1.65.0", Some(95360), Some("renamed to `coroutine_clone`")),
/// Allows defining generators.
(removed, generators, "1.21.0", Some(43122), Some("renamed to `coroutines`")),
/// An extension to the `generic_associated_types` feature, allowing incomplete features.
(removed, generic_associated_types_extended, "CURRENT_RUSTC_VERSION", Some(95451),
Some(
"feature needs overhaul and reimplementation pending \
better implied higher-ranked implied bounds support"
)
),
/// Allows `impl Trait` in bindings (`let`, `const`, `static`).
(removed, impl_trait_in_bindings, "1.55.0", Some(63065),
Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")),

View file

@ -338,7 +338,7 @@ declare_features! (
(unstable, riscv_target_feature, "1.45.0", Some(44839)),
(unstable, rtm_target_feature, "1.35.0", Some(44839)),
(unstable, s390x_target_feature, "1.82.0", Some(44839)),
(unstable, sparc_target_feature, "CURRENT_RUSTC_VERSION", Some(132783)),
(unstable, sparc_target_feature, "1.84.0", Some(132783)),
(unstable, sse4a_target_feature, "1.27.0", Some(44839)),
(unstable, tbm_target_feature, "1.27.0", Some(44839)),
(unstable, wasm_target_feature, "1.30.0", Some(44839)),
@ -394,6 +394,8 @@ declare_features! (
(unstable, async_fn_track_caller, "1.73.0", Some(110011)),
/// Allows `for await` loops.
(unstable, async_for_loop, "1.77.0", Some(118898)),
/// Allows `async` trait bound modifier.
(unstable, async_trait_bounds, "CURRENT_RUSTC_VERSION", Some(62290)),
/// Allows using C-variadics.
(unstable, c_variadic, "1.34.0", Some(44930)),
/// Allows the use of `#[cfg(<true/false>)]`.
@ -475,9 +477,6 @@ declare_features! (
(unstable, exhaustive_patterns, "1.13.0", Some(51085)),
/// Allows explicit tail calls via `become` expression.
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
/// Allows using `efiapi`, `sysv64` and `win64` as calling convention
/// for functions with varargs.
(unstable, extended_varargs_abi_support, "1.65.0", Some(100189)),
/// Allows defining `extern type`s.
(unstable, extern_types, "1.23.0", Some(43467)),
/// Allow using 128-bit (quad precision) floating point numbers.
@ -500,8 +499,6 @@ declare_features! (
(unstable, gen_blocks, "1.75.0", Some(117078)),
/// Infer generic args for both consts and types.
(unstable, generic_arg_infer, "1.55.0", Some(85077)),
/// An extension to the `generic_associated_types` feature, allowing incomplete features.
(incomplete, generic_associated_types_extended, "1.61.0", Some(95451)),
/// Allows non-trivial generic constants which have to have wfness manually propagated to callers
(incomplete, generic_const_exprs, "1.56.0", Some(76560)),
/// Allows generic parameters and where-clauses on free & associated const items.
@ -538,7 +535,7 @@ declare_features! (
/// Allows `#[marker]` on certain traits allowing overlapping implementations.
(unstable, marker_trait_attr, "1.30.0", Some(29864)),
/// Enables the generic const args MVP (only bare paths, not arbitrary computation).
(incomplete, min_generic_const_args, "CURRENT_RUSTC_VERSION", Some(132980)),
(incomplete, min_generic_const_args, "1.84.0", Some(132980)),
/// A minimal, sound subset of specialization intended to be used by the
/// standard library until the soundness issues with specialization
/// are fixed.

View file

@ -109,7 +109,16 @@ pub enum DefKind {
Use,
/// An `extern` block.
ForeignMod,
/// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`
/// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`.
///
/// Not all anon-consts are actually still relevant in the HIR. We lower
/// trivial const-arguments directly to `hir::ConstArgKind::Path`, at which
/// point the definition for the anon-const ends up unused and incomplete.
///
/// We do not provide any a `Span` for the definition and pretty much all other
/// queries also ICE when using this `DefId`. Given that the `DefId` of such
/// constants should only be reachable by iterating all definitions of a
/// given crate, you should not have to worry about this.
AnonConst,
/// An inline constant, e.g. `const { 1 + 2 }`
InlineConst,

View file

@ -1,7 +1,7 @@
use std::fmt;
use rustc_abi::ExternAbi;
use rustc_ast::util::parser::{AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_UNAMBIGUOUS};
use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
use rustc_ast::{
self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
LitKind, TraitObjectSyntax, UintTy,
@ -275,6 +275,7 @@ impl<'hir> ConstArg<'hir> {
match self.kind {
ConstArgKind::Path(path) => path.span(),
ConstArgKind::Anon(anon) => anon.span,
ConstArgKind::Infer(span) => span,
}
}
}
@ -289,6 +290,11 @@ pub enum ConstArgKind<'hir> {
/// However, in the future, we'll be using it for all of those.
Path(QPath<'hir>),
Anon(&'hir AnonConst),
/// **Note:** Not all inferred consts are represented as
/// `ConstArgKind::Infer`. In cases where it is ambiguous whether
/// a generic arg is a type or a const, inference variables are
/// represented as `GenericArg::Infer` instead.
Infer(Span),
}
#[derive(Clone, Copy, Debug, HashStable_Generic)]
@ -308,6 +314,10 @@ pub enum GenericArg<'hir> {
Lifetime(&'hir Lifetime),
Type(&'hir Ty<'hir>),
Const(&'hir ConstArg<'hir>),
/// **Note:** Inference variables are only represented as
/// `GenericArg::Infer` in cases where it is ambiguous whether
/// a generic arg is a type or a const. Otherwise, inference variables
/// are represented as `TyKind::Infer` or `ConstArgKind::Infer`.
Infer(InferArg),
}
@ -1645,29 +1655,6 @@ impl fmt::Display for ConstContext {
/// A literal.
pub type Lit = Spanned<LitKind>;
#[derive(Copy, Clone, Debug, HashStable_Generic)]
pub enum ArrayLen<'hir> {
Infer(InferArg),
Body(&'hir ConstArg<'hir>),
}
impl ArrayLen<'_> {
pub fn span(self) -> Span {
match self {
ArrayLen::Infer(arg) => arg.span,
ArrayLen::Body(body) => body.span(),
}
}
pub fn hir_id(self) -> HirId {
match self {
ArrayLen::Infer(InferArg { hir_id, .. }) | ArrayLen::Body(&ConstArg { hir_id, .. }) => {
hir_id
}
}
}
}
/// A constant (expression) that's not an item or associated item,
/// but needs its own `DefId` for type-checking, const-eval, etc.
/// These are usually found nested inside types (e.g., array lengths)
@ -1708,22 +1695,22 @@ pub struct Expr<'hir> {
}
impl Expr<'_> {
pub fn precedence(&self) -> i8 {
pub fn precedence(&self) -> ExprPrecedence {
match self.kind {
ExprKind::Closure { .. } => PREC_CLOSURE,
ExprKind::Closure { .. } => ExprPrecedence::Closure,
ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::Yield(..)
| ExprKind::Become(..) => PREC_JUMP,
| ExprKind::Become(..) => ExprPrecedence::Jump,
// Binop-like expr kinds, handled by `AssocOp`.
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::Assign(..) |
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
ExprKind::AssignOp(..) => ExprPrecedence::Assign,
// Unary, prefix
ExprKind::AddrOf(..)
@ -1732,7 +1719,7 @@ impl Expr<'_> {
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
// but we need to print `(let _ = a) < b` as-is with parens.
| ExprKind::Let(..)
| ExprKind::Unary(..) => PREC_PREFIX,
| ExprKind::Unary(..) => ExprPrecedence::Prefix,
// Never need parens
ExprKind::Array(_)
@ -1753,7 +1740,7 @@ impl Expr<'_> {
| ExprKind::Struct(..)
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::Err(_) => PREC_UNAMBIGUOUS,
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
}
@ -2115,7 +2102,7 @@ pub enum ExprKind<'hir> {
///
/// E.g., `[1; 5]`. The first expression is the element
/// to be repeated; the second is the number of times to repeat it.
Repeat(&'hir Expr<'hir>, ArrayLen<'hir>),
Repeat(&'hir Expr<'hir>, &'hir ConstArg<'hir>),
/// A suspension point for coroutines (i.e., `yield <expr>`).
Yield(&'hir Expr<'hir>, YieldSource),
@ -2625,7 +2612,7 @@ impl<'hir> Ty<'hir> {
TyKind::Infer => true,
TyKind::Slice(ty) => ty.is_suggestable_infer_ty(),
TyKind::Array(ty, length) => {
ty.is_suggestable_infer_ty() || matches!(length, ArrayLen::Infer(..))
ty.is_suggestable_infer_ty() || matches!(length.kind, ConstArgKind::Infer(..))
}
TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty),
TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(),
@ -2834,7 +2821,7 @@ pub enum TyKind<'hir> {
/// A variable length slice (i.e., `[T]`).
Slice(&'hir Ty<'hir>),
/// A fixed length array (i.e., `[T; n]`).
Array(&'hir Ty<'hir>, ArrayLen<'hir>),
Array(&'hir Ty<'hir>, &'hir ConstArg<'hir>),
/// A raw pointer (i.e., `*const T` or `*mut T`).
Ptr(MutTy<'hir>),
/// A reference (i.e., `&'a T` or `&'a mut T`).
@ -2861,6 +2848,11 @@ pub enum TyKind<'hir> {
Typeof(&'hir AnonConst),
/// `TyKind::Infer` means the type should be inferred instead of it having been
/// specified. This can appear anywhere in a type.
///
/// **Note:** Not all inferred types are represented as
/// `TyKind::Infer`. In cases where it is ambiguous whether
/// a generic arg is a type or a const, inference variables are
/// represented as `GenericArg::Infer` instead.
Infer,
/// Placeholder for a type that has failed to be defined.
Err(rustc_span::ErrorGuaranteed),
@ -3801,8 +3793,6 @@ pub enum Node<'hir> {
Crate(&'hir Mod<'hir>),
Infer(&'hir InferArg),
WherePredicate(&'hir WherePredicate<'hir>),
// FIXME: Merge into `Node::Infer`.
ArrayLenInfer(&'hir InferArg),
PreciseCapturingNonLifetimeArg(&'hir PreciseCapturingNonLifetimeArg),
// Created by query feeding
Synthetic,
@ -3856,7 +3846,6 @@ impl<'hir> Node<'hir> {
| Node::OpaqueTy(..)
| Node::Infer(..)
| Node::WherePredicate(..)
| Node::ArrayLenInfer(..)
| Node::Synthetic
| Node::Err(..) => None,
}

View file

@ -343,9 +343,6 @@ pub trait Visitor<'v>: Sized {
fn visit_pat_field(&mut self, f: &'v PatField<'v>) -> Self::Result {
walk_pat_field(self, f)
}
fn visit_array_length(&mut self, len: &'v ArrayLen<'v>) -> Self::Result {
walk_array_len(self, len)
}
fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result {
walk_anon_const(self, c)
}
@ -710,14 +707,6 @@ pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<'
visitor.visit_pat(field.pat)
}
pub fn walk_array_len<'v, V: Visitor<'v>>(visitor: &mut V, len: &'v ArrayLen<'v>) -> V::Result {
match len {
// FIXME: Use `visit_infer` here.
ArrayLen::Infer(InferArg { hir_id, span: _ }) => visitor.visit_id(*hir_id),
ArrayLen::Body(c) => visitor.visit_const_arg(c),
}
}
pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) -> V::Result {
try_visit!(visitor.visit_id(constant.hir_id));
visitor.visit_nested_body(constant.body)
@ -739,6 +728,7 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>(
match &const_arg.kind {
ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()),
ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon),
ConstArgKind::Infer(..) => V::Result::output(),
}
}
@ -753,7 +743,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
}
ExprKind::Repeat(ref element, ref count) => {
try_visit!(visitor.visit_expr(element));
try_visit!(visitor.visit_array_length(count));
try_visit!(visitor.visit_const_arg(count));
}
ExprKind::Struct(ref qpath, fields, ref optional_base) => {
try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span));
@ -901,7 +891,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
}
TyKind::Array(ref ty, ref length) => {
try_visit!(visitor.visit_ty(ty));
try_visit!(visitor.visit_array_length(length));
try_visit!(visitor.visit_const_arg(length));
}
TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
for bound in bounds {

View file

@ -113,8 +113,6 @@ hir_analysis_const_param_ty_impl_on_unsized =
the trait `ConstParamTy` may not be implemented for this type
.label = type is not `Sized`
hir_analysis_const_specialize = cannot specialize on const impl with non-const impl
hir_analysis_copy_impl_on_non_adt =
the trait `Copy` cannot be implemented for this type
.label = type is not a structure or enumeration
@ -590,7 +588,7 @@ hir_analysis_value_of_associated_struct_already_specified =
.label = re-bound here
.previous_bound_label = `{$item_name}` bound here first
hir_analysis_variadic_function_compatible_convention = C-variadic function must have a compatible calling convention, like {$conventions}
hir_analysis_variadic_function_compatible_convention = C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`
.label = C-variadic function must have a compatible calling convention
hir_analysis_variances_of = {$variances}

View file

@ -17,7 +17,7 @@ use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::span_bug;
use rustc_middle::ty::error::TypeErrorToStringExt;
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::fold::{BottomUpFolder, fold_regions};
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt};
use rustc_middle::ty::{
@ -33,7 +33,7 @@ use tracing::{debug, instrument};
use ty::TypingMode;
use {rustc_attr as attr, rustc_hir as hir};
use super::compare_impl_item::{check_type_bounds, compare_impl_method, compare_impl_ty};
use super::compare_impl_item::check_type_bounds;
use super::*;
use crate::check::intrinsicck::InlineAsmCtxt;
@ -103,7 +103,7 @@ fn allowed_union_or_unsafe_field<'tcx>(
// Fallback case: allow `ManuallyDrop` and things that are `Copy`,
// also no need to report an error if the type is unresolved.
ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
|| ty.is_copy_modulo_regions(tcx, typing_env)
|| tcx.type_is_copy_modulo_regions(typing_env, ty)
|| ty.references_error()
}
};
@ -322,8 +322,12 @@ fn check_opaque_meets_bounds<'tcx>(
};
let param_env = tcx.param_env(defining_use_anchor);
// FIXME(#132279): This should eventually use the already defined hidden types.
let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, defining_use_anchor));
// FIXME(#132279): Once `PostBorrowckAnalysis` is supported in the old solver, this branch should be removed.
let infcx = tcx.infer_ctxt().build(if tcx.next_trait_solver_globally() {
TypingMode::post_borrowck_analysis(tcx, defining_use_anchor)
} else {
TypingMode::analysis_in_body(tcx, defining_use_anchor)
});
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let args = match origin {
@ -346,7 +350,7 @@ fn check_opaque_meets_bounds<'tcx>(
// FIXME: Consider wrapping the hidden type in an existential `Binder` and instantiating it
// here rather than using ReErased.
let hidden_ty = tcx.type_of(def_id.to_def_id()).instantiate(tcx, args);
let hidden_ty = tcx.fold_regions(hidden_ty, |re, _dbi| match re.kind() {
let hidden_ty = fold_regions(tcx, hidden_ty, |re, _dbi| match re.kind() {
ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)),
_ => re,
});
@ -417,7 +421,11 @@ fn check_opaque_meets_bounds<'tcx>(
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
if let hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } = origin {
if infcx.next_trait_solver() {
Ok(())
} else if let hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } =
origin
{
// HACK: this should also fall through to the hidden type check below, but the original
// implementation had a bug where equivalent lifetimes are not identical. This caused us
// to reject existing stable code that is otherwise completely fine. The real fix is to
@ -1036,18 +1044,23 @@ fn check_impl_items_against_trait<'tcx>(
tcx.dcx().span_delayed_bug(tcx.def_span(impl_item), "missing associated item in trait");
continue;
};
match ty_impl_item.kind {
ty::AssocKind::Const => {
tcx.ensure().compare_impl_const((
impl_item.expect_local(),
ty_impl_item.trait_item_def_id.unwrap(),
));
}
ty::AssocKind::Fn => {
compare_impl_method(tcx, ty_impl_item, ty_trait_item, trait_ref);
}
ty::AssocKind::Type => {
compare_impl_ty(tcx, ty_impl_item, ty_trait_item, trait_ref);
let res = tcx.ensure().compare_impl_item(impl_item.expect_local());
if res.is_ok() {
match ty_impl_item.kind {
ty::AssocKind::Fn => {
compare_impl_item::refine::check_refining_return_position_impl_trait_in_trait(
tcx,
ty_impl_item,
ty_trait_item,
tcx.impl_trait_ref(ty_impl_item.container_id(tcx))
.unwrap()
.instantiate_identity(),
);
}
ty::AssocKind::Const => {}
ty::AssocKind::Type => {}
}
}

View file

@ -33,7 +33,25 @@ use tracing::{debug, instrument};
use super::potentially_plural_count;
use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture};
mod refine;
pub(super) mod refine;
/// Call the query `tcx.compare_impl_item()` directly instead.
pub(super) fn compare_impl_item(
tcx: TyCtxt<'_>,
impl_item_def_id: LocalDefId,
) -> Result<(), ErrorGuaranteed> {
let impl_item = tcx.associated_item(impl_item_def_id);
let trait_item = tcx.associated_item(impl_item.trait_item_def_id.unwrap());
let impl_trait_ref =
tcx.impl_trait_ref(impl_item.container_id(tcx)).unwrap().instantiate_identity();
debug!(?impl_trait_ref);
match impl_item.kind {
ty::AssocKind::Fn => compare_impl_method(tcx, impl_item, trait_item, impl_trait_ref),
ty::AssocKind::Type => compare_impl_ty(tcx, impl_item, trait_item, impl_trait_ref),
ty::AssocKind::Const => compare_impl_const(tcx, impl_item, trait_item, impl_trait_ref),
}
}
/// Checks that a method from an impl conforms to the signature of
/// the same method as declared in the trait.
@ -44,22 +62,15 @@ mod refine;
/// - `trait_m`: the method in the trait
/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
#[instrument(level = "debug", skip(tcx))]
pub(super) fn compare_impl_method<'tcx>(
fn compare_impl_method<'tcx>(
tcx: TyCtxt<'tcx>,
impl_m: ty::AssocItem,
trait_m: ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>,
) {
let _: Result<_, ErrorGuaranteed> = try {
check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?;
refine::check_refining_return_position_impl_trait_in_trait(
tcx,
impl_m,
trait_m,
impl_trait_ref,
);
};
) -> Result<(), ErrorGuaranteed> {
check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?;
Ok(())
}
/// Checks a bunch of different properties of the impl/trait methods for
@ -523,8 +534,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
let impl_sig = ocx.normalize(
&misc_cause,
param_env,
tcx.liberate_late_bound_regions(
impl_m.def_id,
infcx.instantiate_binder_with_fresh_vars(
return_span,
infer::HigherRankedType,
tcx.fn_sig(impl_m.def_id).instantiate_identity(),
),
);
@ -536,10 +548,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
// them with inference variables.
// We will use these inference variables to collect the hidden types of RPITITs.
let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_def_id);
let unnormalized_trait_sig = infcx
.instantiate_binder_with_fresh_vars(
return_span,
infer::HigherRankedType,
let unnormalized_trait_sig = tcx
.liberate_late_bound_regions(
impl_m.def_id,
tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_impl_args),
)
.fold_with(&mut collector);
@ -702,8 +713,8 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
let mut remapped_types = DefIdMap::default();
for (def_id, (ty, args)) in collected_types {
match infcx.fully_resolve((ty, args)) {
Ok((ty, args)) => {
match infcx.fully_resolve(ty) {
Ok(ty) => {
// `ty` contains free regions that we created earlier while liberating the
// trait fn signature. However, projection normalization expects `ty` to
// contains `def_id`'s early-bound regions.
@ -883,33 +894,27 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
self.tcx
}
fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
if let ty::Alias(ty::Opaque, ty::AliasTy { args, def_id, .. }) = *t.kind() {
let mut mapped_args = Vec::with_capacity(args.len());
for (arg, v) in std::iter::zip(args, self.tcx.variances_of(def_id)) {
mapped_args.push(match (arg.unpack(), v) {
// Skip uncaptured opaque args
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg,
_ => arg.try_fold_with(self)?,
});
}
Ok(Ty::new_opaque(self.tcx, def_id, self.tcx.mk_args(&mapped_args)))
} else {
t.try_super_fold_with(self)
}
}
fn try_fold_region(
&mut self,
region: ty::Region<'tcx>,
) -> Result<ty::Region<'tcx>, Self::Error> {
match region.kind() {
// Remap late-bound regions from the function.
// Never remap bound regions or `'static`
ty::ReBound(..) | ty::ReStatic | ty::ReError(_) => return Ok(region),
// We always remap liberated late-bound regions from the function.
ty::ReLateParam(_) => {}
// Remap early-bound regions as long as they don't come from the `impl` itself,
// in which case we don't really need to renumber them.
ty::ReEarlyParam(ebr) if ebr.index as usize >= self.num_impl_args => {}
_ => return Ok(region),
ty::ReEarlyParam(ebr) => {
if ebr.index as usize >= self.num_impl_args {
// Remap
} else {
return Ok(region);
}
}
ty::ReVar(_) | ty::RePlaceholder(_) | ty::ReErased => unreachable!(
"should not have leaked vars or placeholders into hidden type of RPITIT"
),
}
let e = if let Some(id_region) = self.map.get(&region) {
@ -1727,17 +1732,12 @@ fn compare_generic_param_kinds<'tcx>(
Ok(())
}
/// Use `tcx.compare_impl_const` instead
pub(super) fn compare_impl_const_raw(
tcx: TyCtxt<'_>,
(impl_const_item_def, trait_const_item_def): (LocalDefId, DefId),
fn compare_impl_const<'tcx>(
tcx: TyCtxt<'tcx>,
impl_const_item: ty::AssocItem,
trait_const_item: ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let impl_const_item = tcx.associated_item(impl_const_item_def);
let trait_const_item = tcx.associated_item(trait_const_item_def);
let impl_trait_ref =
tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity();
debug!(?impl_trait_ref);
compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?;
compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?;
check_region_bounds_on_impl_item(tcx, impl_const_item, trait_const_item, false)?;
@ -1868,19 +1868,17 @@ fn compare_const_predicate_entailment<'tcx>(
}
#[instrument(level = "debug", skip(tcx))]
pub(super) fn compare_impl_ty<'tcx>(
fn compare_impl_ty<'tcx>(
tcx: TyCtxt<'tcx>,
impl_ty: ty::AssocItem,
trait_ty: ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>,
) {
let _: Result<(), ErrorGuaranteed> = try {
compare_number_of_generics(tcx, impl_ty, trait_ty, false)?;
compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?;
check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?;
compare_type_predicate_entailment(tcx, impl_ty, trait_ty, impl_trait_ref)?;
check_type_bounds(tcx, trait_ty, impl_ty, impl_trait_ref)?;
};
) -> Result<(), ErrorGuaranteed> {
compare_number_of_generics(tcx, impl_ty, trait_ty, false)?;
compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?;
check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?;
compare_type_predicate_entailment(tcx, impl_ty, trait_ty, impl_trait_ref)?;
check_type_bounds(tcx, trait_ty, impl_ty, impl_trait_ref)
}
/// The equivalent of [compare_method_predicate_entailment], but for associated types

View file

@ -17,7 +17,7 @@ use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt;
use rustc_trait_selection::traits::{ObligationCtxt, elaborate, normalize_param_env_or_error};
/// Check that an implementation does not refine an RPITIT from a trait method signature.
pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
tcx: TyCtxt<'tcx>,
impl_m: ty::AssocItem,
trait_m: ty::AssocItem,

View file

@ -87,6 +87,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
| sym::assert_inhabited
| sym::assert_zero_valid
| sym::assert_mem_uninitialized_valid
| sym::breakpoint
| sym::size_of
| sym::min_align_of
| sym::needs_drop
@ -641,7 +642,9 @@ pub fn check_intrinsic_type(
| sym::simd_round
| sym::simd_trunc => (1, 0, vec![param(0)], param(0)),
sym::simd_fpowi => (1, 0, vec![param(0), tcx.types.i32], param(0)),
sym::simd_fma => (1, 0, vec![param(0), param(0), param(0)], param(0)),
sym::simd_fma | sym::simd_relaxed_fma => {
(1, 0, vec![param(0), param(0), param(0)], param(0))
}
sym::simd_gather => (3, 0, vec![param(0), param(1), param(2)], param(0)),
sym::simd_masked_load => (3, 0, vec![param(0), param(1), param(2)], param(2)),
sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit),

View file

@ -178,7 +178,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
// Check that the type implements Copy. The only case where this can
// possibly fail is for SIMD types which don't #[derive(Copy)].
if !ty.is_copy_modulo_regions(self.tcx, self.typing_env) {
if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) {
let msg = "arguments for inline assembly must be copyable";
self.tcx
.dcx()

View file

@ -108,7 +108,7 @@ pub fn provide(providers: &mut Providers) {
adt_async_destructor,
region_scope_tree,
collect_return_position_impl_trait_in_trait_tys,
compare_impl_const: compare_impl_item::compare_impl_const_raw,
compare_impl_item: compare_impl_item::compare_impl_item,
check_coroutine_obligations: check::check_coroutine_obligations,
..*providers
};

View file

@ -13,6 +13,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_macros::LintDiagnostic;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::query::Providers;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::trait_def::TraitSpecializationKind;
@ -116,13 +117,12 @@ where
}
f(&mut wfcx)?;
let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
let errors = wfcx.select_all_or_error();
if !errors.is_empty() {
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
}
let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
debug!(?assumed_wf_types);
let infcx_compat = infcx.fork();
@ -1170,19 +1170,13 @@ fn check_type_defn<'tcx>(
// Explicit `enum` discriminant values must const-evaluate successfully.
if let ty::VariantDiscr::Explicit(discr_def_id) = variant.discr {
let cause = traits::ObligationCause::new(
tcx.def_span(discr_def_id),
wfcx.body_def_id,
ObligationCauseCode::Misc,
);
wfcx.register_obligation(Obligation::new(
tcx,
cause,
wfcx.param_env,
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(
ty::Const::from_anon_const(tcx, discr_def_id.expect_local()),
))),
));
match tcx.const_eval_poly(discr_def_id) {
Ok(_) => {}
Err(ErrorHandled::Reported(..)) => {}
Err(ErrorHandled::TooGeneric(sp)) => {
span_bug!(sp, "enum variant discr was too generic to eval")
}
}
}
}

View file

@ -34,6 +34,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::hir::nested_filter;
use rustc_middle::query::Providers;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypingMode};
use rustc_middle::{bug, span_bug};
@ -150,11 +151,11 @@ impl<'v> Visitor<'v> for HirPlaceholderCollector {
_ => {}
}
}
fn visit_array_length(&mut self, length: &'v hir::ArrayLen<'v>) {
if let hir::ArrayLen::Infer(inf) = length {
self.0.push(inf.span);
fn visit_const_arg(&mut self, const_arg: &'v hir::ConstArg<'v>) {
if let hir::ConstArgKind::Infer(span) = const_arg.kind {
self.0.push(span);
}
intravisit::walk_array_len(self, length)
intravisit::walk_const_arg(self, const_arg)
}
}
@ -1415,7 +1416,7 @@ fn infer_return_ty_for_fn_sig<'tcx>(
GenericParamKind::Lifetime { .. } => true,
_ => false,
});
let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r {
let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r {
ty::ReErased => {
if has_region_params {
ty::Region::new_error_with_message(

View file

@ -1,6 +1,6 @@
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::intravisit;
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_span::sym;
@ -42,7 +42,8 @@ pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) {
}
pub(crate) fn def_parents(tcx: TyCtxt<'_>) {
for did in tcx.hir().body_owners() {
for iid in tcx.hir().items() {
let did = iid.owner_id.def_id;
if tcx.has_attr(did, sym::rustc_dump_def_parents) {
struct AnonConstFinder<'tcx> {
tcx: TyCtxt<'tcx>,
@ -50,7 +51,7 @@ pub(crate) fn def_parents(tcx: TyCtxt<'_>) {
}
impl<'tcx> intravisit::Visitor<'tcx> for AnonConstFinder<'tcx> {
type NestedFilter = OnlyBodies;
type NestedFilter = nested_filter::All;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
@ -62,11 +63,11 @@ pub(crate) fn def_parents(tcx: TyCtxt<'_>) {
}
}
// Look for any anon consts inside of this body owner as there is no way to apply
// Look for any anon consts inside of this item as there is no way to apply
// the `rustc_dump_def_parents` attribute to the anon const so it would not be possible
// to see what its def parent is.
let mut anon_ct_finder = AnonConstFinder { tcx, anon_consts: vec![] };
intravisit::walk_expr(&mut anon_ct_finder, tcx.hir().body_owned_by(did).value);
intravisit::walk_item(&mut anon_ct_finder, tcx.hir().item(iid));
for did in [did].into_iter().chain(anon_ct_finder.anon_consts) {
let span = tcx.def_span(did);

View file

@ -181,7 +181,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
// expressions' count (i.e. `N` in `[x; N]`), and explicit
// `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`),
// as they shouldn't be able to cause query cycle errors.
Node::Expr(Expr { kind: ExprKind::Repeat(_, ArrayLen::Body(ct)), .. })
Node::Expr(Expr { kind: ExprKind::Repeat(_, ct), .. })
if ct.anon_const_hir_id() == Some(hir_id) =>
{
Some(parent_did)
@ -419,7 +419,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
if let Node::ConstBlock(_) = node {
own_params.push(ty::GenericParamDef {
index: next_index(),
name: Symbol::intern("<const_ty>"),
name: rustc_span::sym::const_ty_placeholder,
def_id: def_id.to_def_id(),
pure_wrt_drop: false,
kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },

View file

@ -1,12 +1,13 @@
use std::assert_matches::assert_matches;
use hir::{HirId, Node};
use hir::Node;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::ty::{self, GenericPredicates, ImplTraitInTraitData, Ty, TyCtxt, Upcast};
use rustc_middle::ty::{
self, GenericPredicates, ImplTraitInTraitData, Ty, TyCtxt, TypeVisitable, TypeVisitor, Upcast,
};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::Ident;
use rustc_span::{DUMMY_SP, Span};
@ -305,7 +306,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
}
if tcx.features().generic_const_exprs() {
predicates.extend(const_evaluatable_predicates_of(tcx, def_id));
predicates.extend(const_evaluatable_predicates_of(tcx, def_id, &predicates));
}
let mut predicates: Vec<_> = predicates.into_iter().collect();
@ -369,32 +370,48 @@ fn compute_bidirectional_outlives_predicates<'tcx>(
}
}
fn const_evaluatable_predicates_of(
tcx: TyCtxt<'_>,
#[instrument(level = "debug", skip(tcx, predicates), ret)]
fn const_evaluatable_predicates_of<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> FxIndexSet<(ty::Clause<'_>, Span)> {
predicates: &FxIndexSet<(ty::Clause<'tcx>, Span)>,
) -> FxIndexSet<(ty::Clause<'tcx>, Span)> {
struct ConstCollector<'tcx> {
tcx: TyCtxt<'tcx>,
preds: FxIndexSet<(ty::Clause<'tcx>, Span)>,
}
impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> {
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
let ct = ty::Const::from_anon_const(self.tcx, c.def_id);
if let ty::ConstKind::Unevaluated(_) = ct.kind() {
let span = self.tcx.def_span(c.def_id);
self.preds.insert((ty::ClauseKind::ConstEvaluatable(ct).upcast(self.tcx), span));
}
}
fn is_const_param_default(tcx: TyCtxt<'_>, def: LocalDefId) -> bool {
let hir_id = tcx.local_def_id_to_hir_id(def);
let (_, parent_node) = tcx
.hir()
.parent_iter(hir_id)
.skip_while(|(_, n)| matches!(n, Node::ConstArg(..)))
.next()
.unwrap();
matches!(
parent_node,
Node::GenericParam(hir::GenericParam { kind: hir::GenericParamKind::Const { .. }, .. })
)
}
fn visit_const_param_default(&mut self, _param: HirId, _ct: &'tcx hir::ConstArg<'tcx>) {
// Do not look into const param defaults,
// these get checked when they are actually instantiated.
//
// We do not want the following to error:
//
// struct Foo<const N: usize, const M: usize = { N + 1 }>;
// struct Bar<const N: usize>(Foo<N, 3>);
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ConstCollector<'tcx> {
fn visit_const(&mut self, c: ty::Const<'tcx>) {
if let ty::ConstKind::Unevaluated(uv) = c.kind() {
if is_const_param_default(self.tcx, uv.def.expect_local()) {
// Do not look into const param defaults,
// these get checked when they are actually instantiated.
//
// We do not want the following to error:
//
// struct Foo<const N: usize, const M: usize = { N + 1 }>;
// struct Bar<const N: usize>(Foo<N, 3>);
return;
}
let span = self.tcx.def_span(uv.def);
self.preds.insert((ty::ClauseKind::ConstEvaluatable(c).upcast(self.tcx), span));
}
}
}
@ -402,29 +419,32 @@ fn const_evaluatable_predicates_of(
let node = tcx.hir_node(hir_id);
let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() };
for (clause, _sp) in predicates {
clause.visit_with(&mut collector);
}
if let hir::Node::Item(item) = node
&& let hir::ItemKind::Impl(impl_) = item.kind
&& let hir::ItemKind::Impl(_) = item.kind
{
if let Some(of_trait) = &impl_.of_trait {
debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id);
collector.visit_trait_ref(of_trait);
if let Some(of_trait) = tcx.impl_trait_ref(def_id) {
debug!("visit impl trait_ref");
of_trait.instantiate_identity().visit_with(&mut collector);
}
debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id);
collector.visit_ty(impl_.self_ty);
debug!("visit self_ty");
let self_ty = tcx.type_of(def_id);
self_ty.instantiate_identity().visit_with(&mut collector);
}
if let Some(generics) = node.generics() {
debug!("const_evaluatable_predicates_of({:?}): visit_generics", def_id);
collector.visit_generics(generics);
if let Some(_) = tcx.hir().fn_sig_by_hir_id(hir_id) {
debug!("visit fn sig");
let fn_sig = tcx.fn_sig(def_id);
let fn_sig = fn_sig.instantiate_identity();
debug!(?fn_sig);
fn_sig.visit_with(&mut collector);
}
if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) {
debug!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id);
collector.visit_fn_decl(fn_sig.decl);
}
debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds);
collector.preds
}

View file

@ -2060,48 +2060,37 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
};
match path.res {
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => {
// Get the generics of this type's hir owner. This is *different*
// from the generics of the parameter's definition, since we want
// to be able to resolve an RTN path on a nested body (e.g. method
// inside an impl) using the where clauses on the method.
// FIXME(return_type_notation): Think of some better way of doing this.
let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics()
else {
return;
};
// Look for the first bound that contains an associated type that
// matches the segment that we're looking for. We ignore any subsequent
// bounds since we'll be emitting a hard error in HIR lowering, so this
// is purely speculative.
let one_bound = generics.predicates.iter().find_map(|predicate| {
let hir::WherePredicateKind::BoundPredicate(predicate) = predicate.kind
else {
return None;
};
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
predicate.bounded_ty.kind
else {
return None;
};
if bounded_path.res != path.res {
return None;
}
predicate.bounds.iter().find_map(|bound| {
let hir::GenericBound::Trait(trait_) = bound else {
return None;
};
let mut bounds =
self.for_each_trait_bound_on_res(path.res).filter_map(|trait_def_id| {
BoundVarContext::supertrait_hrtb_vars(
self.tcx,
trait_.trait_ref.trait_def_id()?,
trait_def_id,
item_segment.ident,
ty::AssocKind::Fn,
)
})
});
let Some((bound_vars, assoc_item)) = one_bound else {
});
let Some((bound_vars, assoc_item)) = bounds.next() else {
// This will error in HIR lowering.
self.tcx
.dcx()
.span_delayed_bug(path.span, "no resolution for RTN path");
return;
};
// Don't bail if we have identical bounds, which may be collected from
// something like `T: Bound + Bound`, or via elaborating supertraits.
for (second_vars, second_assoc_item) in bounds {
if second_vars != bound_vars || second_assoc_item != assoc_item {
// This will error in HIR lowering.
self.tcx.dcx().span_delayed_bug(
path.span,
"ambiguous resolution for RTN path",
);
return;
}
}
(bound_vars, assoc_item.def_id, item_segment)
}
// If we have a self type alias (in an impl), try to resolve an
@ -2167,6 +2156,92 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
existing_bound_vars.extend(bound_vars);
self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved);
}
/// Walk the generics of the item for a trait bound whose self type
/// corresponds to the expected res, and return the trait def id.
fn for_each_trait_bound_on_res(
&self,
expected_res: Res,
) -> impl Iterator<Item = DefId> + use<'tcx, '_> {
std::iter::from_coroutine(
#[coroutine]
move || {
let mut scope = self.scope;
loop {
let hir_id = match *scope {
Scope::Binder { hir_id, .. } => Some(hir_id),
Scope::Root { opt_parent_item: Some(parent_def_id) } => {
Some(self.tcx.local_def_id_to_hir_id(parent_def_id))
}
Scope::Body { .. }
| Scope::ObjectLifetimeDefault { .. }
| Scope::Supertrait { .. }
| Scope::TraitRefBoundary { .. }
| Scope::LateBoundary { .. }
| Scope::Opaque { .. }
| Scope::Root { opt_parent_item: None } => None,
};
if let Some(hir_id) = hir_id {
let node = self.tcx.hir_node(hir_id);
// If this is a `Self` bound in a trait, yield the trait itself.
// Specifically, we don't need to look at any supertraits since
// we already do that in `BoundVarContext::supertrait_hrtb_vars`.
if let Res::SelfTyParam { trait_: _ } = expected_res
&& let hir::Node::Item(item) = node
&& let hir::ItemKind::Trait(..) = item.kind
{
// Yield the trait's def id. Supertraits will be
// elaborated from that.
yield item.owner_id.def_id.to_def_id();
} else if let Some(generics) = node.generics() {
for pred in generics.predicates {
let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind
else {
continue;
};
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
pred.bounded_ty.kind
else {
continue;
};
// Match the expected res.
if bounded_path.res != expected_res {
continue;
}
for pred in pred.bounds {
match pred {
hir::GenericBound::Trait(poly_trait_ref) => {
if let Some(def_id) =
poly_trait_ref.trait_ref.trait_def_id()
{
yield def_id;
}
}
hir::GenericBound::Outlives(_)
| hir::GenericBound::Use(_, _) => {}
}
}
}
}
}
match *scope {
Scope::Binder { s, .. }
| Scope::Body { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s }
| Scope::LateBoundary { s, .. }
| Scope::Opaque { s, .. } => {
scope = s;
}
Scope::Root { .. } => break,
}
}
},
)
}
}
/// Detects late-bound lifetimes and inserts them into

View file

@ -5,6 +5,7 @@ use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::query::plumbing::CyclePlaceholder;
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
@ -113,7 +114,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
// so no need for ConstArg.
Node::Ty(&hir::Ty { kind: TyKind::Typeof(ref e), span, .. }) if e.hir_id == hir_id => {
let ty = tcx.typeck(def_id).node_type(tcx.local_def_id_to_hir_id(def_id));
let ty = tcx.fold_regions(ty, |r, _| {
let ty = fold_regions(tcx, ty, |r, _| {
if r.is_erased() { ty::Region::new_error_misc(tcx) } else { r }
});
let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false, None) {
@ -144,7 +145,7 @@ fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span
// Easy case: arrays repeat expressions.
Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. })
| Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
if constant.hir_id() == arg_hir_id =>
if constant.hir_id == arg_hir_id =>
{
return tcx.types.usize;
}
@ -578,8 +579,6 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
x => bug!("unexpected non-type Node::GenericParam: {:?}", x),
},
Node::ArrayLenInfer(_) => tcx.types.usize,
x => {
bug!("unexpected sort of node in type_of(): {:?}", x);
}

View file

@ -672,11 +672,10 @@ pub(crate) struct MainFunctionGenericParameters {
#[derive(Diagnostic)]
#[diag(hir_analysis_variadic_function_compatible_convention, code = E0045)]
pub(crate) struct VariadicFunctionCompatibleConvention<'a> {
pub(crate) struct VariadicFunctionCompatibleConvention {
#[primary_span]
#[label]
pub span: Span,
pub conventions: &'a str,
}
#[derive(Diagnostic)]
@ -1080,13 +1079,6 @@ pub(crate) struct EmptySpecialization {
pub base_impl_span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_const_specialize)]
pub(crate) struct ConstSpecialize {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_static_specialize)]
pub(crate) struct StaticSpecialize {

View file

@ -703,7 +703,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
///
/// It might actually be possible that we can already support early-bound generic params
/// in such types if we just lifted some more checks in other places, too, for example
/// inside [`ty::Const::from_anon_const`]. However, even if that were the case, we should
/// inside `HirTyLowerer::lower_anon_const`. However, even if that were the case, we should
/// probably gate this behind another feature flag.
///
/// [^1]: <https://github.com/rust-lang/project-const-generics/issues/28>.

View file

@ -219,11 +219,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id));
}
self.complain_about_missing_assoc_tys(
if let Err(guar) = self.check_for_required_assoc_tys(
associated_types,
potential_assoc_types,
hir_trait_bounds,
);
) {
return Ty::new_error(tcx, guar);
}
// De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
// `dyn Trait + Send`.

View file

@ -714,14 +714,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// reasonable suggestion on how to write it. For the case of multiple associated types in the
/// same trait bound have the same name (as they come from different supertraits), we instead
/// emit a generic note suggesting using a `where` clause to constraint instead.
pub(crate) fn complain_about_missing_assoc_tys(
pub(crate) fn check_for_required_assoc_tys(
&self,
associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
potential_assoc_types: Vec<usize>,
trait_bounds: &[hir::PolyTraitRef<'_>],
) {
) -> Result<(), ErrorGuaranteed> {
if associated_types.values().all(|v| v.is_empty()) {
return;
return Ok(());
}
let tcx = self.tcx();
@ -739,7 +739,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
// `issue-22560.rs`.
let mut trait_bound_spans: Vec<Span> = vec![];
let mut dyn_compatibility_violations = false;
let mut dyn_compatibility_violations = Ok(());
for (span, items) in &associated_types {
if !items.is_empty() {
trait_bound_spans.push(*span);
@ -752,13 +752,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let violations =
dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item);
if !violations.is_empty() {
report_dyn_incompatibility(tcx, *span, None, trait_def_id, &violations).emit();
dyn_compatibility_violations = true;
dyn_compatibility_violations = Err(report_dyn_incompatibility(
tcx,
*span,
None,
trait_def_id,
&violations,
)
.emit());
}
}
}
if dyn_compatibility_violations {
return;
if let Err(guar) = dyn_compatibility_violations {
return Err(guar);
}
// related to issue #91997, turbofishes added only when in an expr or pat
@ -965,7 +972,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
err.emit();
Err(err.emit())
}
/// On ambiguous associated type, look for an associated function whose name matches the

View file

@ -104,7 +104,7 @@ fn generic_arg_mismatch_err(
GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }),
GenericParamDefKind::Const { .. },
) if tcx.type_of(param.def_id).skip_binder() == tcx.types.usize => {
let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id()));
let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id));
if let Ok(snippet) = snippet {
err.span_suggestion(
arg.span(),

View file

@ -37,6 +37,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
use rustc_middle::ty::{
self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt,
@ -1569,7 +1570,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
infcx.fresh_args_for_item(DUMMY_SP, impl_def_id),
);
let value = tcx.fold_regions(qself_ty, |_, _| tcx.lifetimes.re_erased);
let value = fold_regions(tcx, qself_ty, |_, _| tcx.lifetimes.re_erased);
// FIXME: Don't bother dealing with non-lifetime binders here...
if value.has_escaping_bound_vars() {
return false;
@ -2088,7 +2089,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
qpath.span(),
format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
),
hir::ConstArgKind::Anon(anon) => Const::from_anon_const(tcx, anon.def_id),
hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon.def_id),
hir::ConstArgKind::Infer(span) => self.ct_infer(None, span),
}
}
@ -2175,6 +2177,92 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
/// Literals and const generic parameters are eagerly converted to a constant, everything else
/// becomes `Unevaluated`.
#[instrument(skip(self), level = "debug")]
fn lower_anon_const(&self, def: LocalDefId) -> Const<'tcx> {
let tcx = self.tcx();
let body_id = match tcx.hir_node_by_def_id(def) {
hir::Node::AnonConst(ac) => ac.body,
node => span_bug!(
tcx.def_span(def.to_def_id()),
"from_anon_const can only process anonymous constants, not {node:?}"
),
};
let expr = &tcx.hir().body(body_id).value;
debug!(?expr);
let ty = tcx.type_of(def).no_bound_vars().expect("const parameter types cannot be generic");
match self.try_lower_anon_const_lit(ty, expr) {
Some(v) => v,
None => ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst {
def: def.to_def_id(),
args: ty::GenericArgs::identity_for_item(tcx, def.to_def_id()),
}),
}
}
#[instrument(skip(self), level = "debug")]
fn try_lower_anon_const_lit(
&self,
ty: Ty<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
) -> Option<Const<'tcx>> {
let tcx = self.tcx();
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = match &expr.kind {
hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
block.expr.as_ref().unwrap()
}
_ => expr,
};
if let hir::ExprKind::Path(hir::QPath::Resolved(
_,
&hir::Path { res: Res::Def(DefKind::ConstParam, _), .. },
)) = expr.kind
{
span_bug!(
expr.span,
"try_lower_anon_const_lit: received const param which shouldn't be possible"
);
};
let lit_input = match expr.kind {
hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind {
hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: true }),
_ => None,
},
_ => None,
};
if let Some(lit_input) = lit_input {
// If an error occurred, ignore that it's a literal and leave reporting the error up to
// mir.
match tcx.at(expr.span).lit_to_const(lit_input) {
Ok(c) => return Some(c),
Err(_) if lit_input.ty.has_aliases() => {
// allow the `ty` to be an alias type, though we cannot handle it here
return None;
}
Err(e) => {
tcx.dcx().span_delayed_bug(
expr.span,
format!("try_lower_anon_const_lit: couldn't lit_to_const {e:?}"),
);
}
}
}
None
}
fn lower_delegation_ty(&self, idx: hir::InferDelegationKind) -> Ty<'tcx> {
let delegation_sig = self.tcx().inherit_sig_for_delegation_item(self.item_def_id());
match idx {
@ -2309,13 +2397,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
tcx.at(span).type_of(def_id).instantiate(tcx, args)
}
hir::TyKind::Array(ty, length) => {
let length = match length {
hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span),
hir::ArrayLen::Body(constant) => {
self.lower_const_arg(constant, FeedConstTy::No)
}
};
let length = self.lower_const_arg(length, FeedConstTy::No);
Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length)
}
hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(),

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