Merge from rust-lang/rust

This commit is contained in:
Laurențiu Nicola 2024-12-11 11:49:08 +02:00
commit 5db2aa865c
2585 changed files with 34566 additions and 24459 deletions

6
.gitattributes vendored
View file

@ -4,8 +4,10 @@
*.cpp rust
*.h rust
*.rs rust diff=rust
*.fixed linguist-language=Rust
*.mir linguist-language=Rust
*.fixed linguist-language=Rust -merge
*.mir linguist-language=Rust -merge
*.stderr -merge
*.stdout -merge
src/etc/installer/gfx/* binary
src/vendor/** -text
Cargo.lock linguist-generated=false

14
.github/renovate.json5 vendored Normal file
View file

@ -0,0 +1,14 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
// Let Renovatebot keep an opened issue that tracks our dependencies
"dependencyDashboard": true,
// Disable "normal" package updates
"enabledManagers": [],
// Update lockfiles once per week
"lockFileMaintenance": {
"enabled": true,
"schedule": [
"before 5am on Tuesday"
]
}
}

11
.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
@ -89,12 +88,12 @@ package.json
tests/rustdoc-gui/src/**.lock
## direnv
.envrc
.direnv/
/.envrc
/.direnv/
## nix
flake.nix
/flake.nix
flake.lock
default.nix
/default.nix
# Before adding new lines, see the comment at the top.

2
.gitmodules vendored
View file

@ -33,7 +33,7 @@
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
branch = rustc/19.1-2024-09-17
branch = rustc/19.1-2024-12-03
shallow = true
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book

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>

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,7 @@ standard library, and documentation.
## Why Rust?
- **Performance:** Fast and memory-efficient, suitable for critical services, embedded devices, and easily integrate with other languages.
- **Performance:** Fast and memory-efficient, suitable for critical services, embedded devices, and easily integrated with other languages.
- **Reliability:** Our rich type system and ownership model ensure memory and thread safety, reducing bugs at compile-time.

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>
@ -503,7 +503,7 @@ Compatibility Notes
* We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0.
`core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*.
The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
* The new sort implementations may panic if a type's implementation of [`Ord`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) (or the given comparison function) does not implement a [total order](https://en.wikipedia.org/wiki/Total_order) as the trait requires. `Ord`'s supertraits (`PartialOrd`, `Eq`, and `PartialEq`) must also be consistent. The previous implementations would not "notice" any problem, but the new implementations have a good chance of detecting inconsistencies, throwing a panic rather than returning knowingly unsorted data.
@ -584,7 +584,7 @@ Stabilized APIs
- [`impl Default for Arc<CStr>`](https://doc.rust-lang.org/beta/alloc/sync/struct.Arc.html#impl-Default-for-Arc%3CCStr%3E)
- [`impl Default for Arc<[T]>`](https://doc.rust-lang.org/beta/alloc/sync/struct.Arc.html#impl-Default-for-Arc%3C%5BT%5D%3E)
- [`impl IntoIterator for Box<[T]>`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#impl-IntoIterator-for-Box%3C%5BI%5D,+A%3E)
- [`impl FromIterator<String> for Box<str>`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#impl-FromIterator%3CString%3E-for-Box%3Cstr%3E)
- [`impl FromIterator<String> for Box<str>`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#impl-FromIterator%3CString%3E-for-Box%3Cstr%3E)
- [`impl FromIterator<char> for Box<str>`](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#impl-FromIterator%3Cchar%3E-for-Box%3Cstr%3E)
- [`LazyCell`](https://doc.rust-lang.org/beta/core/cell/struct.LazyCell.html)
- [`LazyLock`](https://doc.rust-lang.org/beta/std/sync/struct.LazyLock.html)
@ -1816,7 +1816,7 @@ Compiler
- [Detect uninhabited types early in const eval](https://github.com/rust-lang/rust/pull/109435/)
- [Switch to LLD as default linker for {arm,thumb}v4t-none-eabi](https://github.com/rust-lang/rust/pull/109721/)
- [Add tier 3 target `loongarch64-unknown-linux-gnu`](https://github.com/rust-lang/rust/pull/96971)
- [Add tier 3 target for `i586-pc-nto-qnx700` (QNX Neutrino RTOS, version 7.0)](https://github.com/rust-lang/rust/pull/109173/),
- [Add tier 3 target for `i586-pc-nto-qnx700` (QNX Neutrino RTOS, version 7.0)](https://github.com/rust-lang/rust/pull/109173/),
- [Insert alignment checks for pointer dereferences as debug assertions](https://github.com/rust-lang/rust/pull/98112)
This catches undefined behavior at runtime, and may cause existing code to fail.
@ -2023,7 +2023,7 @@ Compatibility Notes
If `tools = [...]` is set in config.toml, we will respect a missing rustdoc in that list. By
default rustdoc remains included. To retain the prior behavior explicitly add `"rustdoc"` to the
list.
<a id="1.69.0-Internal-Changes"></a>
Internal Changes

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:
@ -629,9 +627,11 @@ impl Pat {
| PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
// Trivial wrappers over inner patterns.
PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => {
s.walk(it)
}
PatKind::Box(s)
| PatKind::Deref(s)
| PatKind::Ref(s, _)
| PatKind::Paren(s)
| PatKind::Guard(s, _) => s.walk(it),
// These patterns do not contain subpatterns, skip.
PatKind::Wild
@ -841,6 +841,9 @@ pub enum PatKind {
// A never pattern `!`.
Never,
/// A guard pattern (e.g., `x if guard(x)`).
Guard(P<Pat>, P<Expr>),
/// Parentheses in patterns used for grouping (i.e., `(PAT)`).
Paren(P<Pat>),
@ -1184,14 +1187,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 +1320,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 +1351,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 +1384,7 @@ impl Expr {
| ExprKind::Underscore
| ExprKind::While(..)
| ExprKind::Err(_)
| ExprKind::Dummy => PREC_UNAMBIGUOUS,
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
}
}
@ -1732,12 +1736,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 +1753,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 +1795,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 +1809,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);
}
@ -2546,6 +2571,18 @@ pub enum SelfKind {
Explicit(P<Ty>, Mutability),
}
impl SelfKind {
pub fn to_ref_suggestion(&self) -> String {
match self {
SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(),
SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()),
SelfKind::Value(_) | SelfKind::Explicit(_, _) => {
unreachable!("if we had an explicit self, we wouldn't be here")
}
}
}
}
pub type ExplicitSelf = Spanned<SelfKind>;
impl Param {
@ -3082,6 +3119,7 @@ pub struct FieldDef {
pub ident: Option<Ident>,
pub ty: P<Ty>,
pub default: Option<AnonConst>,
pub is_placeholder: bool,
}

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)
}
}
}
@ -1123,13 +1120,14 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
}
pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) {
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd;
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd;
visitor.visit_id(id);
visit_attrs(visitor, attrs);
visitor.visit_vis(vis);
visit_safety(visitor, safety);
visit_opt(ident, |ident| visitor.visit_ident(ident));
visitor.visit_ty(ty);
visit_opt(default, |default| visitor.visit_anon_const(default));
visitor.visit_span(span);
}
@ -1528,6 +1526,10 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
visit_opt(e2, |e| vis.visit_expr(e));
vis.visit_span(span);
}
PatKind::Guard(p, e) => {
vis.visit_pat(p);
vis.visit_expr(e);
}
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
visit_thin_vec(elems, |elem| vis.visit_pat(elem))
}
@ -1628,9 +1630,10 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
visit_thin_exprs(vis, call_args);
vis.visit_span(span);
}
ExprKind::Binary(_binop, lhs, rhs) => {
ExprKind::Binary(binop, lhs, rhs) => {
vis.visit_expr(lhs);
vis.visit_expr(rhs);
vis.visit_span(&mut binop.span);
}
ExprKind::Unary(_unop, ohs) => vis.visit_expr(ohs),
ExprKind::Cast(expr, ty) => {
@ -1788,20 +1791,21 @@ pub fn noop_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Optio
pub fn walk_flat_map_stmt<T: MutVisitor>(
vis: &mut T,
Stmt { kind, mut span, mut id }: Stmt,
Stmt { kind, span, mut id }: Stmt,
) -> SmallVec<[Stmt; 1]> {
vis.visit_id(&mut id);
let stmts: SmallVec<_> = walk_flat_map_stmt_kind(vis, kind)
let mut stmts: SmallVec<[Stmt; 1]> = walk_flat_map_stmt_kind(vis, kind)
.into_iter()
.map(|kind| Stmt { id, kind, span })
.collect();
if stmts.len() > 1 {
panic!(
match stmts.len() {
0 => {}
1 => vis.visit_span(&mut stmts[0].span),
2.. => panic!(
"cloning statement `NodeId`s is prohibited by default, \
the visitor should implement custom statement visiting"
);
),
}
vis.visit_span(&mut span);
stmts
}

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

@ -682,6 +682,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
visit_opt!(visitor, visit_expr, lower_bound);
visit_opt!(visitor, visit_expr, upper_bound);
}
PatKind::Guard(subpattern, guard_condition) => {
try_visit!(visitor.visit_pat(subpattern));
try_visit!(visitor.visit_expr(guard_condition));
}
PatKind::Wild | PatKind::Rest | PatKind::Never => {}
PatKind::Err(_guar) => {}
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
@ -971,11 +975,13 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(
}
pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result {
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field;
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _, default } =
field;
walk_list!(visitor, visit_attribute, attrs);
try_visit!(visitor.visit_vis(vis));
visit_opt!(visitor, visit_ident, ident);
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_anon_const, &*default);
V::Result::output()
}
@ -1273,10 +1279,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

@ -45,10 +45,6 @@ ast_lowering_bad_return_type_notation_output =
ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
ast_lowering_base_expression_double_dot =
base expression required after `..`
.suggestion = add a base expression here
ast_lowering_clobber_abi_not_supported =
`clobber_abi` is not supported on this target
@ -57,6 +53,9 @@ ast_lowering_closure_cannot_be_static = closures cannot be static
ast_lowering_coroutine_too_many_parameters =
too many parameters for a coroutine (expected 0 or 1 parameters)
ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs
.label = default fields are only supported on structs
ast_lowering_does_not_support_modifiers =
the `{$class_name}` register class does not support template modifiers

View file

@ -227,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

@ -38,6 +38,14 @@ pub(crate) struct InvalidAbi {
pub suggestion: Option<InvalidAbiSuggestion>,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_default_field_in_tuple)]
pub(crate) struct TupleStructWithDefault {
#[primary_span]
#[label]
pub span: Span,
}
pub(crate) struct InvalidAbiReason(pub &'static str);
impl Subdiagnostic for InvalidAbiReason {
@ -114,14 +122,6 @@ pub(crate) struct UnderscoreExprLhsAssign {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_base_expression_double_dot, code = E0797)]
pub(crate) struct BaseExpressionDoubleDot {
#[primary_span]
#[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {

View file

@ -19,10 +19,10 @@ use thin_vec::{ThinVec, thin_vec};
use visit::{Visitor, walk_expr};
use super::errors::{
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
ClosureCannotBeStatic, CoroutineTooManyParameters,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic,
CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment,
InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard,
UnderscoreExprLhsAssign,
};
use super::{
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
@ -109,9 +109,7 @@ 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)
@ -359,12 +357,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
),
ExprKind::Struct(se) => {
let rest = match &se.rest {
StructRest::Base(e) => Some(self.lower_expr(e)),
StructRest::Rest(sp) => {
let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp });
Some(&*self.arena.alloc(self.expr_err(*sp, guar)))
}
StructRest::None => None,
StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)),
StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp),
StructRest::None => hir::StructTailExpr::None,
};
hir::ExprKind::Struct(
self.arena.alloc(self.lower_qpath(
@ -452,15 +447,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 +748,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 +1043,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 +1150,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);
@ -1540,7 +1523,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Struct(
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
fields,
None,
hir::StructTailExpr::None,
)
}

View file

@ -17,7 +17,10 @@ use smallvec::{SmallVec, smallvec};
use thin_vec::ThinVec;
use tracing::instrument;
use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound};
use super::errors::{
InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound,
TupleStructWithDefault,
};
use super::{
AstOwner, FnDeclKind, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
ResolverAstLoweringExt,
@ -690,13 +693,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
VariantData::Tuple(fields, id) => {
let ctor_id = self.lower_node_id(*id);
self.alias_attrs(ctor_id, parent_id);
hir::VariantData::Tuple(
self.arena.alloc_from_iter(
fields.iter().enumerate().map(|f| self.lower_field_def(f)),
),
ctor_id,
self.local_def_id(*id),
)
let fields = self
.arena
.alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f)));
for field in &fields[..] {
if let Some(default) = field.default {
// Default values in tuple struct and tuple variants are not allowed by the
// RFC due to concerns about the syntax, both in the item definition and the
// expression. We could in the future allow `struct S(i32 = 0);` and force
// users to construct the value with `let _ = S { .. };`.
if self.tcx.features().default_field_values() {
self.dcx().emit_err(TupleStructWithDefault { span: default.span });
} else {
let _ = self.dcx().span_delayed_bug(
default.span,
"expected `default values on `struct` fields aren't supported` \
feature-gate error but none was produced",
);
}
}
}
hir::VariantData::Tuple(fields, ctor_id, self.local_def_id(*id))
}
VariantData::Unit(id) => {
let ctor_id = self.lower_node_id(*id);
@ -723,6 +740,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
},
vis_span: self.lower_span(f.vis.span),
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
ty,
safety: self.lower_safety(f.safety, hir::Safety::Safe),
}

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) }
}
}
}
@ -1467,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)
}
@ -2035,7 +2011,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ExprKind::Underscore => {
if self.tcx.features().generic_arg_infer() {
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span));
self.arena.alloc(hir::ConstArg { hir_id: self.next_id(), kind: ct_kind })
self.arena
.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
} else {
feature_err(
&self.tcx.sess,
@ -2083,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);
@ -2107,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,
})
});
@ -2158,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);
@ -2168,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

@ -114,6 +114,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_range_end(end, e2.is_some()),
);
}
// FIXME(guard_patterns): lower pattern guards to HIR
PatKind::Guard(inner, _) => pattern = inner,
PatKind::Slice(pats) => break self.lower_pat_slice(pats),
PatKind::Rest => {
// If we reach here the `..` pattern is not semantically allowed.

View file

@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing
ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
ast_passes_show_span = {$msg}
ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library
ast_passes_static_without_body =

View file

@ -779,14 +779,6 @@ pub(crate) struct IncompatibleFeatures {
pub f2: Symbol,
}
#[derive(Diagnostic)]
#[diag(ast_passes_show_span)]
pub(crate) struct ShowSpan {
#[primary_span]
pub span: Span,
pub msg: &'static str,
}
#[derive(Diagnostic)]
#[diag(ast_passes_negative_bound_not_supported)]
pub(crate) struct NegativeBoundUnsupported {

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,
@ -551,6 +556,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
gate_all!(default_field_values, "default values on fields are experimental");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
@ -690,6 +697,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

@ -1,8 +1,6 @@
//! The `rustc_ast_passes` crate contains passes which validate the AST in `syntax`
//! parsed by `rustc_parse` and then lowered, after the passes in this crate,
//! by `rustc_ast_lowering`.
//!
//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.
// tidy-alphabetical-start
#![allow(internal_features)]
@ -18,6 +16,5 @@
pub mod ast_validation;
mod errors;
pub mod feature_gate;
pub mod show_span;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

View file

@ -1,68 +0,0 @@
//! Span debugger
//!
//! This module shows spans for all expressions in the crate
//! to help with compiler debugging.
use std::str::FromStr;
use rustc_ast as ast;
use rustc_ast::visit;
use rustc_ast::visit::Visitor;
use rustc_errors::DiagCtxtHandle;
use crate::errors;
enum Mode {
Expression,
Pattern,
Type,
}
impl FromStr for Mode {
type Err = ();
fn from_str(s: &str) -> Result<Mode, ()> {
let mode = match s {
"expr" => Mode::Expression,
"pat" => Mode::Pattern,
"ty" => Mode::Type,
_ => return Err(()),
};
Ok(mode)
}
}
struct ShowSpanVisitor<'a> {
dcx: DiagCtxtHandle<'a>,
mode: Mode,
}
impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
fn visit_expr(&mut self, e: &'a ast::Expr) {
if let Mode::Expression = self.mode {
self.dcx.emit_warn(errors::ShowSpan { span: e.span, msg: "expression" });
}
visit::walk_expr(self, e);
}
fn visit_pat(&mut self, p: &'a ast::Pat) {
if let Mode::Pattern = self.mode {
self.dcx.emit_warn(errors::ShowSpan { span: p.span, msg: "pattern" });
}
visit::walk_pat(self, p);
}
fn visit_ty(&mut self, t: &'a ast::Ty) {
if let Mode::Type = self.mode {
self.dcx.emit_warn(errors::ShowSpan { span: t.span, msg: "type" });
}
visit::walk_ty(self, t);
}
}
pub fn run(dcx: DiagCtxtHandle<'_>, mode: &str, krate: &ast::Crate) {
let Ok(mode) = mode.parse() else {
return;
};
let mut v = ShowSpanVisitor { dcx, mode };
visit::walk_crate(&mut v, krate);
}

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("=");
@ -1709,6 +1709,14 @@ impl<'a> State<'a> {
self.print_expr(e, FixupContext::default());
}
}
PatKind::Guard(subpat, condition) => {
self.popen();
self.print_pat(subpat);
self.space();
self.word_space("if");
self.print_expr(condition, FixupContext::default());
self.pclose();
}
PatKind::Slice(elts) => {
self.word("[");
self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));

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

@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
expr: &hir::Expr<'_>,
) {
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return };
let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) =
expr.kind
else {
return;
};
let hir::QPath::Resolved(_, path) = struct_qpath else { return };
let hir::def::Res::Def(_, def_id) = path.res else { return };
let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
@ -1239,7 +1243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
use_spans: Option<UseSpans<'tcx>>,
) {
if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind {
// We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
// `Location` that covers both the `S { ... }` literal, all of its fields and the
// `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`
@ -1450,6 +1454,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
ty::Param(param_ty) => Ok((
generics.type_param(param_ty, tcx),
predicate.trait_ref.print_trait_sugared().to_string(),
Some(predicate.trait_ref.def_id),
)),
_ => Err(()),
}
@ -1463,9 +1468,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
tcx,
hir_generics,
err,
predicates
.iter()
.map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
predicates.iter().map(|(param, constraint, def_id)| {
(param.name.as_str(), &**constraint, *def_id)
}),
None,
);
}

View file

@ -26,7 +26,7 @@ use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::Diag;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
use rustc_index::bit_set::{BitSet, MixedBitSet};
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
@ -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,
@ -1796,7 +1797,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'tcx>, Span),
maybe_uninits: &ChunkedBitSet<MovePathIndex>,
maybe_uninits: &MixedBitSet<MovePathIndex>,
from: u64,
to: u64,
) {

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;
@ -972,25 +973,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'tcx>>,
) -> bool {
let tcx = infcx.tcx;
let TypeTest { generic_kind, lower_bound, span: _, verify_bound: _ } = type_test;
let TypeTest { generic_kind, lower_bound, span: blame_span, ref verify_bound } = *type_test;
let generic_ty = generic_kind.to_ty(tcx);
let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else {
return false;
};
debug!("subject = {:?}", subject);
let r_scc = self.constraint_sccs.scc(*lower_bound);
let r_scc = self.constraint_sccs.scc(lower_bound);
debug!(
"lower_bound = {:?} r_scc={:?} universe={:?}",
lower_bound,
r_scc,
self.constraint_sccs.annotation(r_scc).min_universe()
);
// If the type test requires that `T: 'a` where `'a` is a
// placeholder from another universe, that effectively requires
// `T: 'static`, so we have to propagate that requirement.
@ -1003,7 +999,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
subject,
outlived_free_region: static_r,
blame_span: type_test.span,
blame_span,
category: ConstraintCategory::Boring,
});
@ -1030,12 +1026,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// where `ur` is a local bound -- we are sometimes in a
// position to prove things that our caller cannot. See
// #53570 for an example.
if self.eval_verify_bound(infcx, generic_ty, ur, &type_test.verify_bound) {
if self.eval_verify_bound(infcx, generic_ty, ur, &verify_bound) {
continue;
}
let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur);
debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub);
debug!(?non_local_ub);
// This is slightly too conservative. To show T: '1, given `'2: '1`
// and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to
@ -1048,10 +1044,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let requirement = ClosureOutlivesRequirement {
subject,
outlived_free_region: upper_bound,
blame_span: type_test.span,
blame_span,
category: ConstraintCategory::Boring,
};
debug!("try_promote_type_test: pushing {:#?}", requirement);
debug!(?requirement, "adding closure requirement");
propagated_outlives_requirements.push(requirement);
}
}
@ -1062,45 +1058,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// variables in the type `T` with an equal universal region from the
/// closure signature.
/// This is not always possible, so this is a fallible process.
#[instrument(level = "debug", skip(self, infcx))]
#[instrument(level = "debug", skip(self, infcx), ret)]
fn try_promote_type_test_subject(
&self,
infcx: &InferCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<ClosureOutlivesSubject<'tcx>> {
let tcx = infcx.tcx;
// Opaque types' args may include useless lifetimes.
// We will replace them with ReStatic.
struct OpaqueFolder<'tcx> {
tcx: TyCtxt<'tcx>,
}
impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for OpaqueFolder<'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
use ty::TypeSuperFoldable as _;
let tcx = self.tcx;
let &ty::Alias(ty::Opaque, ty::AliasTy { args, def_id, .. }) = t.kind() else {
return t.super_fold_with(self);
};
let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| {
match (arg.unpack(), v) {
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => {
tcx.lifetimes.re_static.into()
}
_ => arg.fold_with(self),
}
});
Ty::new_opaque(tcx, def_id, tcx.mk_args_from_iter(args))
}
}
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 +1239,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

@ -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) => {
@ -2073,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

@ -94,6 +94,21 @@ builtin_macros_cfg_accessible_indeterminate = cannot determine whether the path
builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a literal
builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified
builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified
builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized`
builtin_macros_coerce_pointee_requires_one_field = `CoercePointee` can only be derived on `struct`s with at least one field
builtin_macros_coerce_pointee_requires_one_generic = `CoercePointee` can only be derived on `struct`s that are generic over at least one type
builtin_macros_coerce_pointee_requires_one_pointee = exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits
builtin_macros_coerce_pointee_requires_transparent = `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`
builtin_macros_coerce_pointee_too_many_pointees = only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits
.label = here another type parameter is marked as `#[pointee]`
builtin_macros_concat_bytes_array = cannot concatenate doubly nested array
.note = byte strings are treated as arrays of bytes
.help = try flattening the array
@ -246,7 +261,7 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive
builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants{$post}
.help = consider a manual implementation of `Default`
builtin_macros_only_one_argument = {$name} takes 1 argument

View file

@ -9,6 +9,7 @@ use rustc_ast::{
use rustc_attr as attr;
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_macros::Diagnostic;
use rustc_span::symbol::{Ident, sym};
use rustc_span::{Span, Symbol};
use thin_vec::{ThinVec, thin_vec};
@ -38,12 +39,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
.any(|r| matches!(r, attr::ReprTransparent))
});
if !is_transparent {
cx.dcx()
.struct_span_err(
span,
"`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`",
)
.emit();
cx.dcx().emit_err(RequireTransparent { span });
return;
}
if !matches!(
@ -51,22 +47,12 @@ pub(crate) fn expand_deriving_coerce_pointee(
VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _)
if !fields.is_empty())
{
cx.dcx()
.struct_span_err(
span,
"`CoercePointee` can only be derived on `struct`s with at least one field",
)
.emit();
cx.dcx().emit_err(RequireOneField { span });
return;
}
(aitem.ident, g)
} else {
cx.dcx()
.struct_span_err(
span,
"`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`",
)
.emit();
cx.dcx().emit_err(RequireTransparent { span });
return;
};
@ -95,10 +81,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
let pointee_param_idx = if type_params.is_empty() {
// `#[derive(CoercePointee)]` requires at least one generic type on the target `struct`
cx.dcx().struct_span_err(
span,
"`CoercePointee` can only be derived on `struct`s that are generic over at least one type",
).emit();
cx.dcx().emit_err(RequireOneGeneric { span });
return;
} else if type_params.len() == 1 {
// Regardless of the only type param being designed as `#[pointee]` or not, we can just use it as such
@ -111,19 +94,11 @@ pub(crate) fn expand_deriving_coerce_pointee(
match (pointees.next(), pointees.next()) {
(Some((idx, _span)), None) => idx,
(None, _) => {
cx.dcx().struct_span_err(
span,
"exactly one generic type parameter must be marked as #[pointee] to derive CoercePointee traits",
).emit();
cx.dcx().emit_err(RequireOnePointee { span });
return;
}
(Some((_, one)), Some((_, another))) => {
cx.dcx()
.struct_span_err(
vec![one, another],
"only one type parameter can be marked as `#[pointee]` when deriving CoercePointee traits",
)
.emit();
cx.dcx().emit_err(TooManyPointees { one, another });
return;
}
}
@ -181,15 +156,10 @@ pub(crate) fn expand_deriving_coerce_pointee(
pointee_ty_ident.name,
)
{
cx.dcx()
.struct_span_err(
pointee_ty_ident.span,
format!(
"`derive(CoercePointee)` requires {} to be marked `?Sized`",
pointee_ty_ident.name
),
)
.emit();
cx.dcx().emit_err(RequiresMaybeSized {
span: pointee_ty_ident.span,
name: pointee_ty_ident.name.to_ident_string(),
});
return;
}
let arg = GenericArg::Type(s_ty.clone());
@ -459,3 +429,48 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b>
}
}
}
#[derive(Diagnostic)]
#[diag(builtin_macros_coerce_pointee_requires_transparent)]
struct RequireTransparent {
#[primary_span]
span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_coerce_pointee_requires_one_field)]
struct RequireOneField {
#[primary_span]
span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_coerce_pointee_requires_one_generic)]
struct RequireOneGeneric {
#[primary_span]
span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_coerce_pointee_requires_one_pointee)]
struct RequireOnePointee {
#[primary_span]
span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_coerce_pointee_too_many_pointees)]
struct TooManyPointees {
#[primary_span]
one: Span,
#[label]
another: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_coerce_pointee_requires_maybe_sized)]
struct RequiresMaybeSized {
#[primary_span]
span: Span,
name: String,
}

View file

@ -205,7 +205,7 @@ where
let fields = fields
.iter()
.enumerate()
.map(|(i, &(ident, span))| {
.map(|(i, &(ident, span, _))| {
let arg = getarg(cx, span, ident.name, i);
cx.field_imm(span, ident, arg)
})

View file

@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default(
trait_def.expand(cx, mitem, item, push)
}
fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<ast::Expr> {
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
cx.expr_call_global(span, default_ident, ThinVec::new())
}
fn default_struct_substructure(
cx: &ExtCtxt<'_>,
trait_span: Span,
substr: &Substructure<'_>,
summary: &StaticFields,
) -> BlockOrExpr {
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ThinVec::new());
let expr = match summary {
Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
Unnamed(fields, IsTuple::Yes) => {
let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect();
cx.expr_call_ident(trait_span, substr.type_ident, exprs)
}
Named(fields) => {
let default_fields = fields
.iter()
.map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
.map(|(ident, span, default_val)| {
let value = match default_val {
// We use `Default::default()`.
None => default_call(cx, *span),
// We use the field default const expression.
Some(val) => {
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
}
};
cx.field_imm(*span, *ident, value)
})
.collect();
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
}
@ -93,10 +105,38 @@ fn default_enum_substructure(
} {
Ok(default_variant) => {
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
cx.expr_path(cx.path(default_variant.span, vec![
Ident::new(kw::SelfUpper, default_variant.span),
default_variant.ident,
]))
match &default_variant.data {
VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![
Ident::new(kw::SelfUpper, default_variant.span),
default_variant.ident,
])),
VariantData::Struct { fields, .. } => {
// This only happens if `#![feature(default_field_values)]`. We have validated
// all fields have default values in the definition.
let default_fields = fields
.iter()
.map(|field| {
cx.field_imm(field.span, field.ident.unwrap(), match &field.default {
// We use `Default::default()`.
None => default_call(cx, field.span),
// We use the field default const expression.
Some(val) => {
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
}
})
})
.collect();
let path = cx.path(default_variant.span, vec![
Ident::new(kw::SelfUpper, default_variant.span),
default_variant.ident,
]);
cx.expr_struct(default_variant.span, path, default_fields)
}
// Logic error in `extract_default_variant`.
VariantData::Tuple(..) => {
cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
}
}
}
Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
};
@ -156,8 +196,20 @@ fn extract_default_variant<'a>(
}
};
if !matches!(variant.data, VariantData::Unit(..)) {
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
if cx.ecfg.features.default_field_values()
&& let VariantData::Struct { fields, .. } = &variant.data
&& fields.iter().all(|f| f.default.is_some())
// Disallow `#[default] Variant {}`
&& !fields.is_empty()
{
// Allowed
} else if !matches!(variant.data, VariantData::Unit(..)) {
let post = if cx.ecfg.features.default_field_values() {
" or variants where every field has a default value"
} else {
""
};
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post });
return Err(guar);
}
@ -216,7 +268,12 @@ struct DetectNonVariantDefaultAttr<'a, 'b> {
impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
if attr.has_name(kw::Default) {
self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span });
let post = if self.cx.ecfg.features.default_field_values() {
" or variants where every field has a default value"
} else {
""
};
self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post });
}
rustc_ast::visit::walk_attribute(self, attr);

View file

@ -182,8 +182,8 @@ pub(crate) use StaticFields::*;
pub(crate) use SubstructureFields::*;
use rustc_ast::ptr::P;
use rustc_ast::{
self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
Mutability, PatKind, VariantData,
self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
Generics, Mutability, PatKind, VariantData,
};
use rustc_attr as attr;
use rustc_expand::base::{Annotatable, ExtCtxt};
@ -296,7 +296,7 @@ pub(crate) enum StaticFields {
/// Tuple and unit structs/enum variants like this.
Unnamed(Vec<Span>, IsTuple),
/// Normal structs/struct variants.
Named(Vec<(Ident, Span)>),
Named(Vec<(Ident, Span, Option<AnonConst>)>),
}
/// A summary of the possible sets of fields.
@ -1435,7 +1435,7 @@ impl<'a> TraitDef<'a> {
for field in struct_def.fields() {
let sp = field.span.with_ctxt(self.span.ctxt());
match field.ident {
Some(ident) => named_idents.push((ident, sp)),
Some(ident) => named_idents.push((ident, sp, field.default.clone())),
_ => just_spans.push(sp),
}
}

View file

@ -424,6 +424,7 @@ pub(crate) struct MultipleDefaultsSugg {
pub(crate) struct NonUnitDefault {
#[primary_span]
pub(crate) span: Span,
pub(crate) post: &'static str,
}
#[derive(Diagnostic)]

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

@ -46,24 +46,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cranelift-bforest"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5e7afe85cadb55c4c1176268a2ac046fdff8dfaeca39e18581b9dc319ca9e"
checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-bitset"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ab25ef3be935a80680e393183e1f94ef507e93a24a8369494d2c6818aedb3e3"
checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156"
[[package]]
name = "cranelift-codegen"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900a19b84545924f1851cbfe386962edfc4ecbc3366a254825cf1ecbcda8ba08"
checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b"
dependencies = [
"bumpalo",
"cranelift-bforest",
@ -78,48 +78,49 @@ dependencies = [
"log",
"regalloc2",
"rustc-hash",
"serde",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-codegen-meta"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c73b2395ffe9e7b4fdf7e2ebc052e7e27af13f68a964985346be4da477a5fc"
checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d9ed0854e96a4ff0879bff39d078de8dea7f002721c9494c1fdb4e1baa86ccc"
checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895"
[[package]]
name = "cranelift-control"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4aca921dd422e781409de0129c255768fec5dec1dae83239b497fb9138abb89"
checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec"
dependencies = [
"arbitrary",
]
[[package]]
name = "cranelift-entity"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d770e6605eccee15b49decdd82cd26f2b6404767802471459ea49c57379a98"
checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96"
dependencies = [
"cranelift-bitset",
]
[[package]]
name = "cranelift-frontend"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29268711cb889cb39215b10faf88b9087d4c9e1d2633581e4f722a2bf4bb4ef9"
checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e"
dependencies = [
"cranelift-codegen",
"log",
@ -129,15 +130,15 @@ dependencies = [
[[package]]
name = "cranelift-isle"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc65156f010aed1985767ad1bff0eb8d186743b7b03e23d0c17604a253e3f356"
checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8"
[[package]]
name = "cranelift-jit"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ba6b46367a4f466cfb1abe32793fa1a0f96d862251491b01a44726b8ed9445"
checksum = "62699329d4ced20fe281fbaef45e11b473b7ab310491b4bdebcd8b818a8ef7fe"
dependencies = [
"anyhow",
"cranelift-codegen",
@ -155,9 +156,9 @@ dependencies = [
[[package]]
name = "cranelift-module"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "007607022a4883ebdffc46c0925e2e10babf2a565ae78518034ade722aa825d2"
checksum = "2f20b0b51ba962dac30fc7e812b86e4390d908acd4f59bcc8ac7610a8f3e0977"
dependencies = [
"anyhow",
"cranelift-codegen",
@ -166,9 +167,9 @@ dependencies = [
[[package]]
name = "cranelift-native"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8bf9b361eaf5a7627647270fabf1dc910d993edbeaf272a652c107861ebe9c2"
checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f"
dependencies = [
"cranelift-codegen",
"libc",
@ -177,9 +178,9 @@ dependencies = [
[[package]]
name = "cranelift-object"
version = "0.113.0"
version = "0.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ca5c38fa00c0cd943035391bdcc84ed00748f17c66c682e410f5a62f234d44"
checksum = "ee231640a7ecceedd0f1f2782d9288db6a6908cc70675ed9427e3bf0ea6daacd"
dependencies = [
"anyhow",
"cranelift-codegen",
@ -363,6 +364,26 @@ dependencies = [
"target-lexicon",
]
[[package]]
name = "serde"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "slice-group-by"
version = "0.3.1"
@ -412,9 +433,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasmtime-jit-icache-coherence"
version = "26.0.0"
version = "27.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e458e6a1a010a53f86ac8d75837c0c6b2ce3e54b7503b2f1dc5629a4a541f5a"
checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad"
dependencies = [
"anyhow",
"cfg-if",

View file

@ -8,12 +8,12 @@ crate-type = ["dylib"]
[dependencies]
# These have to be in sync with each other
cranelift-codegen = { version = "0.113.0", default-features = false, features = ["std", "unwind", "all-native-arch"] }
cranelift-frontend = { version = "0.113.0" }
cranelift-module = { version = "0.113.0" }
cranelift-native = { version = "0.113.0" }
cranelift-jit = { version = "0.113.0", optional = true }
cranelift-object = { version = "0.113.0" }
cranelift-codegen = { version = "0.114.0", default-features = false, features = ["std", "unwind", "all-native-arch"] }
cranelift-frontend = { version = "0.114.0" }
cranelift-module = { version = "0.114.0" }
cranelift-native = { version = "0.114.0" }
cranelift-jit = { version = "0.114.0", optional = true }
cranelift-object = { version = "0.114.0" }
target-lexicon = "0.12.0"
gimli = { version = "0.31", default-features = false, features = ["write"] }
object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }

View file

@ -1,4 +1,4 @@
use crate::path::{Dirs, RelPath};
use crate::path::Dirs;
use crate::prepare::GitRepo;
use crate::utils::{CargoProject, Compiler, spawn_and_wait};
use crate::{CodegenBackend, SysrootKind, build_sysroot};
@ -20,7 +20,7 @@ pub(crate) fn run(
rustup_toolchain_name: Option<&str>,
bootstrap_host_compiler: &Compiler,
) {
RelPath::DOWNLOAD.ensure_exists(dirs);
std::fs::create_dir_all(&dirs.download_dir).unwrap();
ABI_CAFE_REPO.fetch(dirs);
ABI_CAFE_REPO.patch(dirs);

View file

@ -3,7 +3,7 @@ use std::io::Write;
use std::path::Path;
use std::process::Command;
use crate::path::{Dirs, RelPath};
use crate::path::Dirs;
use crate::prepare::GitRepo;
use crate::rustc_info::get_file_name;
use crate::utils::{Compiler, spawn_and_wait};
@ -39,11 +39,11 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
};
eprintln!("[BENCH COMPILE] ebobby/simple-raytracer");
let cargo_clif = RelPath::DIST
.to_path(dirs)
let cargo_clif = dirs
.dist_dir
.join(get_file_name(&bootstrap_host_compiler.rustc, "cargo_clif", "bin").replace('_', "-"));
let manifest_path = SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).join("Cargo.toml");
let target_dir = RelPath::BUILD.join("simple_raytracer").to_path(dirs);
let target_dir = dirs.build_dir.join("simple_raytracer");
let clean_cmd = format!(
"RUSTC=rustc cargo clean --manifest-path {manifest_path} --target-dir {target_dir}",
@ -68,7 +68,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
target_dir = target_dir.display(),
);
let bench_compile_markdown = RelPath::DIST.to_path(dirs).join("bench_compile.md");
let bench_compile_markdown = dirs.dist_dir.join("bench_compile.md");
let bench_compile = hyperfine_command(
1,
@ -92,7 +92,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
eprintln!("[BENCH RUN] ebobby/simple-raytracer");
let bench_run_markdown = RelPath::DIST.to_path(dirs).join("bench_run.md");
let bench_run_markdown = dirs.dist_dir.join("bench_run.md");
let raytracer_cg_llvm = Path::new(".").join(get_file_name(
&bootstrap_host_compiler.rustc,
@ -120,7 +120,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
],
&bench_run_markdown,
);
bench_run.current_dir(RelPath::BUILD.to_path(dirs));
bench_run.current_dir(&dirs.build_dir);
spawn_and_wait(bench_run);
if let Some(gha_step_summary) = gha_step_summary.as_mut() {

View file

@ -6,7 +6,7 @@ use crate::rustc_info::get_file_name;
use crate::shared_utils::{rustflags_from_env, rustflags_to_cmd_env};
use crate::utils::{CargoProject, Compiler, LogGroup};
static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif");
static CG_CLIF: CargoProject = CargoProject::new(&RelPath::source("."), "cg_clif");
pub(crate) fn build_backend(
dirs: &Dirs,

View file

@ -22,9 +22,9 @@ pub(crate) fn build_sysroot(
eprintln!("[BUILD] sysroot {:?}", sysroot_kind);
let dist_dir = RelPath::DIST.to_path(dirs);
let dist_dir = &dirs.dist_dir;
ensure_empty_dir(&dist_dir);
ensure_empty_dir(dist_dir);
fs::create_dir_all(dist_dir.join("bin")).unwrap();
fs::create_dir_all(dist_dir.join("lib")).unwrap();
@ -55,7 +55,7 @@ pub(crate) fn build_sysroot(
let mut build_cargo_wrapper_cmd = Command::new(&bootstrap_host_compiler.rustc);
let wrapper_path = dist_dir.join(&wrapper_name);
build_cargo_wrapper_cmd
.arg(RelPath::SCRIPTS.to_path(dirs).join(&format!("{wrapper}.rs")))
.arg(dirs.source_dir.join("scripts").join(&format!("{wrapper}.rs")))
.arg("-o")
.arg(&wrapper_path)
.arg("-Cstrip=debuginfo");
@ -85,7 +85,7 @@ pub(crate) fn build_sysroot(
&cg_clif_dylib_path,
sysroot_kind,
);
host.install_into_sysroot(&dist_dir);
host.install_into_sysroot(dist_dir);
if !is_native {
build_sysroot_for_triple(
@ -99,7 +99,7 @@ pub(crate) fn build_sysroot(
&cg_clif_dylib_path,
sysroot_kind,
)
.install_into_sysroot(&dist_dir);
.install_into_sysroot(dist_dir);
}
let mut target_compiler = {
@ -143,10 +143,10 @@ impl SysrootTarget {
}
}
static STDLIB_SRC: RelPath = RelPath::BUILD.join("stdlib");
static STDLIB_SRC: RelPath = RelPath::build("stdlib");
static STANDARD_LIBRARY: CargoProject =
CargoProject::new(&STDLIB_SRC.join("library/sysroot"), "stdlib_target");
static RTSTARTUP_SYSROOT: RelPath = RelPath::BUILD.join("rtstartup");
CargoProject::new(&RelPath::build("stdlib/library/sysroot"), "stdlib_target");
static RTSTARTUP_SYSROOT: RelPath = RelPath::build("rtstartup");
fn build_sysroot_for_triple(
dirs: &Dirs,
@ -247,6 +247,7 @@ fn build_clif_sysroot_for_triple(
let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs);
build_cmd.arg("--release");
build_cmd.arg("--features").arg("backtrace panic-unwind compiler-builtins-no-f16-f128");
build_cmd.arg(format!("-Zroot-dir={}", STDLIB_SRC.to_path(dirs).display()));
build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true");
build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif");
if compiler.triple.contains("apple") {
@ -281,13 +282,14 @@ fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option<SysrootTarget> {
return None;
}
RTSTARTUP_SYSROOT.ensure_fresh(dirs);
let rtstartup_sysroot = RTSTARTUP_SYSROOT.to_path(dirs);
ensure_empty_dir(&rtstartup_sysroot);
let rtstartup_src = STDLIB_SRC.to_path(dirs).join("library").join("rtstartup");
let mut target_libs = SysrootTarget { triple: compiler.triple.clone(), libs: vec![] };
for file in ["rsbegin", "rsend"] {
let obj = RTSTARTUP_SYSROOT.to_path(dirs).join(format!("{file}.o"));
let obj = rtstartup_sysroot.join(format!("{file}.o"));
let mut build_rtstartup_cmd = Command::new(&compiler.rustc);
build_rtstartup_cmd
.arg("--target")

View file

@ -185,12 +185,11 @@ fn main() {
frozen,
};
path::RelPath::BUILD.ensure_exists(&dirs);
std::fs::create_dir_all(&dirs.build_dir).unwrap();
{
// Make sure we always explicitly specify the target dir
let target =
path::RelPath::BUILD.join("target_dir_should_be_set_explicitly").to_path(&dirs);
let target = dirs.build_dir.join("target_dir_should_be_set_explicitly");
env::set_var("CARGO_TARGET_DIR", &target);
let _ = std::fs::remove_file(&target);
std::fs::File::create(target).unwrap();

View file

@ -1,8 +1,5 @@
use std::fs;
use std::path::PathBuf;
use crate::utils::ensure_empty_dir;
#[derive(Debug, Clone)]
pub(crate) struct Dirs {
pub(crate) source_dir: PathBuf,
@ -16,54 +13,34 @@ pub(crate) struct Dirs {
#[derive(Debug, Copy, Clone)]
pub(crate) enum PathBase {
Source,
Download,
Build,
Dist,
}
impl PathBase {
fn to_path(self, dirs: &Dirs) -> PathBuf {
match self {
PathBase::Source => dirs.source_dir.clone(),
PathBase::Download => dirs.download_dir.clone(),
PathBase::Build => dirs.build_dir.clone(),
PathBase::Dist => dirs.dist_dir.clone(),
}
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum RelPath {
Base(PathBase),
Join(&'static RelPath, &'static str),
pub(crate) struct RelPath {
base: PathBase,
suffix: &'static str,
}
impl RelPath {
pub(crate) const SOURCE: RelPath = RelPath::Base(PathBase::Source);
pub(crate) const DOWNLOAD: RelPath = RelPath::Base(PathBase::Download);
pub(crate) const BUILD: RelPath = RelPath::Base(PathBase::Build);
pub(crate) const DIST: RelPath = RelPath::Base(PathBase::Dist);
pub(crate) const fn source(suffix: &'static str) -> RelPath {
RelPath { base: PathBase::Source, suffix }
}
pub(crate) const SCRIPTS: RelPath = RelPath::SOURCE.join("scripts");
pub(crate) const PATCHES: RelPath = RelPath::SOURCE.join("patches");
pub(crate) const fn join(&'static self, suffix: &'static str) -> RelPath {
RelPath::Join(self, suffix)
pub(crate) const fn build(suffix: &'static str) -> RelPath {
RelPath { base: PathBase::Build, suffix }
}
pub(crate) fn to_path(&self, dirs: &Dirs) -> PathBuf {
match self {
RelPath::Base(base) => base.to_path(dirs),
RelPath::Join(base, suffix) => base.to_path(dirs).join(suffix),
}
}
pub(crate) fn ensure_exists(&self, dirs: &Dirs) {
fs::create_dir_all(self.to_path(dirs)).unwrap();
}
pub(crate) fn ensure_fresh(&self, dirs: &Dirs) {
let path = self.to_path(dirs);
ensure_empty_dir(&path);
self.base.to_path(dirs).join(self.suffix)
}
}

View file

@ -8,7 +8,7 @@ use crate::path::{Dirs, RelPath};
use crate::utils::{copy_dir_recursively, ensure_empty_dir, spawn_and_wait};
pub(crate) fn prepare(dirs: &Dirs) {
RelPath::DOWNLOAD.ensure_exists(dirs);
std::fs::create_dir_all(&dirs.download_dir).unwrap();
crate::tests::RAND_REPO.fetch(dirs);
crate::tests::REGEX_REPO.fetch(dirs);
}
@ -79,13 +79,13 @@ impl GitRepo {
fn download_dir(&self, dirs: &Dirs) -> PathBuf {
match self.url {
GitRepoUrl::Github { user: _, repo } => RelPath::DOWNLOAD.join(repo).to_path(dirs),
GitRepoUrl::Github { user: _, repo } => dirs.download_dir.join(repo),
}
}
pub(crate) const fn source_dir(&self) -> RelPath {
match self.url {
GitRepoUrl::Github { user: _, repo } => RelPath::BUILD.join(repo),
GitRepoUrl::Github { user: _, repo } => RelPath::build(repo),
}
}
@ -130,7 +130,7 @@ impl GitRepo {
}
let source_lockfile =
RelPath::PATCHES.to_path(dirs).join(format!("{}-lock.toml", self.patch_name));
dirs.source_dir.join("patches").join(format!("{}-lock.toml", self.patch_name));
let target_lockfile = download_dir.join("Cargo.lock");
if source_lockfile.exists() {
assert!(!target_lockfile.exists());
@ -191,7 +191,7 @@ fn init_git_repo(repo_dir: &Path) {
}
fn get_patches(dirs: &Dirs, crate_name: &str) -> Vec<PathBuf> {
let mut patches: Vec<_> = fs::read_dir(RelPath::PATCHES.to_path(dirs))
let mut patches: Vec<_> = fs::read_dir(dirs.source_dir.join("patches"))
.unwrap()
.map(|entry| entry.unwrap().path())
.filter(|path| path.extension() == Some(OsStr::new("patch")))

View file

@ -7,10 +7,10 @@ use crate::path::{Dirs, RelPath};
use crate::prepare::{GitRepo, apply_patches};
use crate::rustc_info::get_default_sysroot;
use crate::shared_utils::rustflags_from_env;
use crate::utils::{CargoProject, Compiler, LogGroup, spawn_and_wait};
use crate::utils::{CargoProject, Compiler, LogGroup, ensure_empty_dir, spawn_and_wait};
use crate::{CodegenBackend, SysrootKind, build_sysroot, config};
static BUILD_EXAMPLE_OUT_DIR: RelPath = RelPath::BUILD.join("example");
static BUILD_EXAMPLE_OUT_DIR: RelPath = RelPath::build("example");
struct TestCase {
config: &'static str,
@ -92,10 +92,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[
TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]),
TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]),
TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"),
TestCase::custom("aot.polymorphize_coroutine", &|runner| {
runner.run_rustc(&["example/polymorphize_coroutine.rs", "-Zpolymorphize"]);
runner.run_out_command("polymorphize_coroutine", &[]);
}),
TestCase::build_bin_and_run("aot.neon", "example/neon.rs", &[]),
TestCase::custom("aot.gen_block_iterate", &|runner| {
runner.run_rustc([
@ -129,11 +125,11 @@ pub(crate) static REGEX_REPO: GitRepo = GitRepo::github(
static REGEX: CargoProject = CargoProject::new(&REGEX_REPO.source_dir(), "regex_target");
static PORTABLE_SIMD_SRC: RelPath = RelPath::BUILD.join("portable-simd");
static PORTABLE_SIMD_SRC: RelPath = RelPath::build("portable-simd");
static PORTABLE_SIMD: CargoProject = CargoProject::new(&PORTABLE_SIMD_SRC, "portable-simd_target");
static LIBCORE_TESTS_SRC: RelPath = RelPath::BUILD.join("coretests");
static LIBCORE_TESTS_SRC: RelPath = RelPath::build("coretests");
static LIBCORE_TESTS: CargoProject = CargoProject::new(&LIBCORE_TESTS_SRC, "coretests_target");
@ -162,7 +158,7 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
&LIBCORE_TESTS_SRC.to_path(&runner.dirs),
);
let source_lockfile = RelPath::PATCHES.to_path(&runner.dirs).join("coretests-lock.toml");
let source_lockfile = runner.dirs.source_dir.join("patches/coretests-lock.toml");
let target_lockfile = LIBCORE_TESTS_SRC.to_path(&runner.dirs).join("Cargo.lock");
fs::copy(source_lockfile, target_lockfile).unwrap();
@ -267,7 +263,9 @@ pub(crate) fn run_tests(
stdlib_source.clone(),
);
BUILD_EXAMPLE_OUT_DIR.ensure_fresh(dirs);
let path = BUILD_EXAMPLE_OUT_DIR.to_path(dirs);
ensure_empty_dir(&path);
runner.run_testsuite(NO_SYSROOT_SUITE);
} else {
eprintln!("[SKIP] no_sysroot tests");

View file

@ -93,7 +93,7 @@ impl CargoProject {
}
pub(crate) fn target_dir(&self, dirs: &Dirs) -> PathBuf {
RelPath::BUILD.join(self.target).to_path(dirs)
dirs.build_dir.join(self.target)
}
#[must_use]

View file

@ -42,7 +42,6 @@ aot.float-minmax-pass
aot.mod_bench
aot.issue-72793
aot.issue-59326
aot.polymorphize_coroutine
aot.neon
aot.gen_block_iterate
aot.raw-dylib

View file

@ -55,26 +55,26 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
impl<T: ?Sized> LegacyReceiver for Box<T> {}
#[lang = "copy"]
pub unsafe trait Copy {}
pub trait Copy {}
unsafe impl Copy for bool {}
unsafe impl Copy for u8 {}
unsafe impl Copy for u16 {}
unsafe impl Copy for u32 {}
unsafe impl Copy for u64 {}
unsafe impl Copy for u128 {}
unsafe impl Copy for usize {}
unsafe impl Copy for i8 {}
unsafe impl Copy for i16 {}
unsafe impl Copy for i32 {}
unsafe impl Copy for isize {}
unsafe impl Copy for f32 {}
unsafe impl Copy for f64 {}
unsafe impl Copy for char {}
unsafe impl<'a, T: ?Sized> Copy for &'a T {}
unsafe impl<T: ?Sized> Copy for *const T {}
unsafe impl<T: ?Sized> Copy for *mut T {}
unsafe impl<T: Copy> Copy for Option<T> {}
impl Copy for bool {}
impl Copy for u8 {}
impl Copy for u16 {}
impl Copy for u32 {}
impl Copy for u64 {}
impl Copy for u128 {}
impl Copy for usize {}
impl Copy for i8 {}
impl Copy for i16 {}
impl Copy for i32 {}
impl Copy for isize {}
impl Copy for f32 {}
impl Copy for f64 {}
impl Copy for char {}
impl<'a, T: ?Sized> Copy for &'a T {}
impl<T: ?Sized> Copy for *const T {}
impl<T: ?Sized> Copy for *mut T {}
impl<T: Copy> Copy for Option<T> {}
#[lang = "sync"]
pub unsafe trait Sync {}

View file

@ -1,17 +0,0 @@
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
use std::ops::Coroutine;
use std::pin::Pin;
fn main() {
run_coroutine::<i32>();
}
fn run_coroutine<T>() {
let mut coroutine = #[coroutine]
|| {
yield;
return;
};
Pin::new(&mut coroutine).resume(());
}

View file

@ -8,6 +8,9 @@
unboxed_closures
)]
#![allow(internal_features)]
// FIXME once abi_unsupported_vector_types is a hard error disable the foo test when the respective
// target feature is not enabled.
#![allow(abi_unsupported_vector_types)]
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;

View file

@ -38,7 +38,7 @@ index 42a26ae..5ac1042 100644
@@ -1,3 +1,4 @@
+#![cfg(test)]
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(const_three_way_compare))]
#![cfg_attr(bootstrap, feature(strict_provenance))]
#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
#![cfg_attr(test, feature(cfg_match))]
--
2.21.0 (Apple Git-122)

View file

@ -14,10 +14,9 @@ diff --git a/lib.rs b/lib.rs
index 1e336bf..35e6f54 100644
--- a/lib.rs
+++ b/lib.rs
@@ -2,7 +2,6 @@
#![cfg_attr(bootstrap, feature(const_three_way_compare))]
#![cfg_attr(bootstrap, feature(strict_provenance))]
#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))]
@@ -2,6 +2,5 @@
#![cfg(test)]
// tidy-alphabetical-start
-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
#![cfg_attr(test, feature(cfg_match))]
#![feature(alloc_layout_extra)]

View file

@ -4,22 +4,23 @@ Date: Fri, 9 Aug 2024 15:44:51 +0000
Subject: [PATCH] Disable f16 and f128 in compiler-builtins
---
library/sysroot/Cargo.toml | 2 +-
library/liballoc/Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml
diff --git a/library/liballoc/Cargo.toml b/library/liballoc/Cargo.toml
index 7165c3e48af..968552ad435 100644
--- a/library/sysroot/Cargo.toml
+++ b/library/sysroot/Cargo.toml
--- a/library/alloc/Cargo.toml
+++ b/library/alloc/Cargo.toml
@@ -11,7 +11,7 @@ test = { path = "../test" }
edition = "2021"
# Forward features to the `std` crate as necessary
[features]
-default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"]
+default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind", "compiler-builtins-no-f16-f128"]
backtrace = ["std/backtrace"]
compiler-builtins-c = ["std/compiler-builtins-c"]
compiler-builtins-mem = ["std/compiler-builtins-mem"]
[dependencies]
core = { path = "../core" }
-compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std', 'no-f16-f128'] }
[dev-dependencies]
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
--
2.34.1

View file

@ -1,4 +1,4 @@
[toolchain]
channel = "nightly-2024-11-09"
channel = "nightly-2024-12-06"
components = ["rust-src", "rustc-dev", "llvm-tools"]
profile = "minimal"

View file

@ -33,6 +33,11 @@ fn main() {
args.push(OsString::from("--sysroot"));
args.push(OsString::from(sysroot.to_str().unwrap()));
}
if passed_args.is_empty() {
// Don't pass any arguments when the user didn't pass any arguments
// either to ensure the help message is shown.
args.clear();
}
args.extend(passed_args);
let rustc = if let Some(rustc) = option_env!("RUSTC") {

View file

@ -35,13 +35,14 @@ full-bootstrap = true
local-rebuild = true
[rust]
download-rustc = false
codegen-backends = ["cranelift"]
deny-warnings = false
verbose-tests = false
# The cg_clif sysroot doesn't contain llvm tools and unless llvm_tools is
# disabled bootstrap will crash trying to copy llvm tools for the bootstrap
# compiler.
llvm_tools = false
llvm-tools = false
EOF
popd

View file

@ -11,22 +11,5 @@ rm -r compiler/rustc_codegen_cranelift/{Cargo.*,src}
cp ../Cargo.* compiler/rustc_codegen_cranelift/
cp -r ../src compiler/rustc_codegen_cranelift/src
# FIXME(rust-lang/rust#132719) remove once it doesn't break without this patch
cat <<EOF | git apply -
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 3394f2a84a0..cb980dd4d7c 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1976,7 +1976,7 @@ fn run(self, builder: &Builder<'_>) -> Compiler {
}
}
- {
+ if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled {
// \`llvm-strip\` is used by rustc, which is actually just a symlink to \`llvm-objcopy\`,
// so copy and rename \`llvm-objcopy\`.
let src_exe = exe("llvm-objcopy", target_compiler.host);
EOF
./x.py build --stage 1 library/std
popd

View file

@ -57,6 +57,7 @@ rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't
rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported
rm tests/ui/simd/simd-bitmask-notpow2.rs # non-pow-of-2 simd vector sizes
rm -r tests/run-make/embed-source-dwarf # embedding sources in debuginfo
rm tests/ui/simd-abi-checks.rs # vector types >128bits not yet supported
# requires LTO
rm -r tests/run-make/cdylib
@ -75,6 +76,8 @@ rm -r tests/ui/instrument-coverage/
rm tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs
rm tests/ui/asm/aarch64/type-f16.rs
rm tests/ui/float/conv-bits-runtime-const.rs
rm tests/ui/consts/const-eval/float_methods.rs
rm tests/ui/match/match-float.rs
# optimization tests
# ==================

View file

@ -125,8 +125,9 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
returns: Vec<AbiParam>,
args: &[Value],
) -> Cow<'_, [Value]> {
if self.tcx.sess.target.is_like_windows {
let (mut params, mut args): (Vec<_>, Vec<_>) = params
// Pass i128 arguments by-ref on Windows.
let (params, args): (Vec<_>, Cow<'_, [_]>) = if self.tcx.sess.target.is_like_windows {
let (params, args): (Vec<_>, Vec<_>) = params
.into_iter()
.zip(args)
.map(|(param, &arg)| {
@ -140,29 +141,42 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
})
.unzip();
let indirect_ret_val = returns.len() == 1 && returns[0].value_type == types::I128;
(params, args.into())
} else {
(params, args.into())
};
if indirect_ret_val {
params.insert(0, AbiParam::new(self.pointer_type));
let ret_ptr = self.create_stack_slot(16, 16);
args.insert(0, ret_ptr.get_addr(self));
self.lib_call_unadjusted(name, params, vec![], &args);
return Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]);
// Return i128 using a return area pointer on Windows and s390x.
let adjust_ret_param =
if self.tcx.sess.target.is_like_windows || self.tcx.sess.target.arch == "s390x" {
returns.len() == 1 && returns[0].value_type == types::I128
} else {
return self.lib_call_unadjusted(name, params, returns, &args);
}
}
false
};
self.lib_call_unadjusted(name, params, returns, args)
if adjust_ret_param {
let mut params = params;
let mut args = args.to_vec();
params.insert(0, AbiParam::new(self.pointer_type));
let ret_ptr = self.create_stack_slot(16, 16);
args.insert(0, ret_ptr.get_addr(self));
self.lib_call_unadjusted(name, params, vec![], &args);
Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())])
} else {
Cow::Borrowed(self.lib_call_unadjusted(name, params, returns, &args))
}
}
pub(crate) fn lib_call_unadjusted(
fn lib_call_unadjusted(
&mut self,
name: &str,
params: Vec<AbiParam>,
returns: Vec<AbiParam>,
args: &[Value],
) -> Cow<'_, [Value]> {
) -> &[Value] {
let sig = Signature { params, returns, call_conv: self.target_config.default_call_conv };
let func_id = self.module.declare_function(name, Linkage::Import, &sig).unwrap();
let func_ref = self.module.declare_func_in_func(func_id, &mut self.bcx.func);
@ -175,7 +189,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
}
let results = self.bcx.inst_results(call_inst);
assert!(results.len() <= 2, "{}", results.len());
Cow::Borrowed(results)
results
}
}
@ -380,8 +394,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
def_id,
fn_args,
source_info.span,
)
.polymorphize(fx.tcx);
);
if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) {
if target.is_some() {
@ -684,7 +697,7 @@ pub(crate) fn codegen_drop<'tcx>(
target: BasicBlock,
) {
let ty = drop_place.layout().ty;
let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx);
let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty);
if let ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) =
drop_instance.def

View file

@ -6,6 +6,7 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_module::ModuleError;
use rustc_ast::InlineAsmOptions;
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_index::IndexVec;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::InlineAsmMacro;
@ -16,6 +17,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use crate::constant::ConstantCx;
use crate::debuginfo::{FunctionDebugContext, TypeDebugContext};
use crate::enable_verifier;
use crate::inline_asm::codegen_naked_asm;
use crate::prelude::*;
use crate::pretty_clif::CommentWriter;
@ -169,12 +171,13 @@ pub(crate) fn codegen_fn<'tcx>(
pub(crate) fn compile_fn(
cx: &mut crate::CodegenCx,
profiler: &SelfProfilerRef,
cached_context: &mut Context,
module: &mut dyn Module,
codegened_func: CodegenedFunction,
) {
let _timer =
cx.profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name);
profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name);
let clif_comments = codegened_func.clif_comments;
@ -212,7 +215,7 @@ pub(crate) fn compile_fn(
};
// Define function
cx.profiler.generic_activity("define function").run(|| {
profiler.generic_activity("define function").run(|| {
context.want_disasm = cx.should_write_ir;
match module.define_function(codegened_func.func_id, context) {
Ok(()) => {}
@ -253,7 +256,7 @@ pub(crate) fn compile_fn(
// Define debuginfo for function
let debug_context = &mut cx.debug_context;
cx.profiler.generic_activity("generate debug info").run(|| {
profiler.generic_activity("generate debug info").run(|| {
if let Some(debug_context) = debug_context {
codegened_func.func_debug_cx.unwrap().finalize(
debug_context,
@ -264,11 +267,11 @@ pub(crate) fn compile_fn(
});
}
pub(crate) fn verify_func(
tcx: TyCtxt<'_>,
writer: &crate::pretty_clif::CommentWriter,
func: &Function,
) {
fn verify_func(tcx: TyCtxt<'_>, writer: &crate::pretty_clif::CommentWriter, func: &Function) {
if !enable_verifier(tcx.sess) {
return;
}
tcx.prof.generic_activity("verify clif ir").run(|| {
let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
match cranelift_codegen::verify_function(&func, &flags) {
@ -670,8 +673,7 @@ fn codegen_stmt<'tcx>(
def_id,
args,
)
.unwrap()
.polymorphize(fx.tcx),
.unwrap(),
);
let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref);
lval.write_cvalue(fx, CValue::by_val(func_addr, to_layout));
@ -757,8 +759,7 @@ fn codegen_stmt<'tcx>(
def_id,
args,
ty::ClosureKind::FnOnce,
)
.polymorphize(fx.tcx);
);
let func_ref = fx.get_function_ref(instance);
let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref);
lval.write_cvalue(fx, CValue::by_val(func_addr, lval.layout()));
@ -1084,7 +1085,7 @@ fn codegen_panic_inner<'tcx>(
let def_id = fx.tcx.require_lang_item(lang_item, span);
let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx);
let instance = Instance::mono(fx.tcx, def_id);
if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) {
fx.bcx.ins().trap(TrapCode::user(2).unwrap());

View file

@ -81,26 +81,6 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
match bin_op {
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(),
BinOp::Add | BinOp::Sub => None,
BinOp::Mul if is_signed => {
let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32));
let lhs = lhs.load_scalar(fx);
let rhs = rhs.load_scalar(fx);
let oflow_ptr = oflow.to_ptr().get_addr(fx);
let res = fx.lib_call_unadjusted(
"__muloti4",
vec![
AbiParam::new(types::I128),
AbiParam::new(types::I128),
AbiParam::new(fx.pointer_type),
],
vec![AbiParam::new(types::I128)],
&[lhs, rhs, oflow_ptr],
)[0];
let oflow = oflow.to_cvalue(fx).load_scalar(fx);
let oflow = fx.bcx.ins().ireduce(types::I8, oflow);
Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty)))
}
BinOp::Mul => {
let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty));
@ -110,7 +90,12 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
AbiParam::new(types::I128),
];
let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)];
fx.lib_call("__rust_u128_mulo", param_types, vec![], &args);
fx.lib_call(
if is_signed { "__rust_i128_mulo" } else { "__rust_u128_mulo" },
param_types,
vec![],
&args,
);
Some(out_place.to_cvalue(fx))
}
BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),

View file

@ -1,10 +1,3 @@
use std::env;
use std::str::FromStr;
fn bool_env_var(key: &str) -> bool {
env::var(key).as_deref() == Ok("1")
}
/// The mode to use for compilation.
#[derive(Copy, Clone, Debug)]
pub enum CodegenMode {
@ -16,19 +9,6 @@ pub enum CodegenMode {
JitLazy,
}
impl FromStr for CodegenMode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"aot" => Ok(CodegenMode::Aot),
"jit" => Ok(CodegenMode::Jit),
"jit-lazy" => Ok(CodegenMode::JitLazy),
_ => Err(format!("Unknown codegen mode `{}`", s)),
}
}
}
/// Configuration of cg_clif as passed in through `-Cllvm-args` and various env vars.
#[derive(Clone, Debug)]
pub struct BackendConfig {
@ -41,51 +21,22 @@ pub struct BackendConfig {
///
/// Defaults to the value of `CG_CLIF_JIT_ARGS`.
pub jit_args: Vec<String>,
/// Enable the Cranelift ir verifier for all compilation passes. If not set it will only run
/// once before passing the clif ir to Cranelift for compilation.
///
/// Defaults to true when the `CG_CLIF_ENABLE_VERIFIER` env var is set to 1 or when cg_clif is
/// compiled with debug assertions enabled or false otherwise. Can be set using
/// `-Cllvm-args=enable_verifier=...`.
pub enable_verifier: bool,
/// Don't cache object files in the incremental cache. Useful during development of cg_clif
/// to make it possible to use incremental mode for all analyses performed by rustc without
/// caching object files when their content should have been changed by a change to cg_clif.
///
/// Defaults to true when the `CG_CLIF_DISABLE_INCR_CACHE` env var is set to 1 or false
/// otherwise. Can be set using `-Cllvm-args=disable_incr_cache=...`.
pub disable_incr_cache: bool,
}
impl Default for BackendConfig {
fn default() -> Self {
BackendConfig {
codegen_mode: CodegenMode::Aot,
jit_args: {
match std::env::var("CG_CLIF_JIT_ARGS") {
Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(),
Err(std::env::VarError::NotPresent) => vec![],
Err(std::env::VarError::NotUnicode(s)) => {
panic!("CG_CLIF_JIT_ARGS not unicode: {:?}", s);
}
}
},
enable_verifier: cfg!(debug_assertions) || bool_env_var("CG_CLIF_ENABLE_VERIFIER"),
disable_incr_cache: bool_env_var("CG_CLIF_DISABLE_INCR_CACHE"),
}
}
}
impl BackendConfig {
/// Parse the configuration passed in using `-Cllvm-args`.
pub fn from_opts(opts: &[String]) -> Result<Self, String> {
fn parse_bool(name: &str, value: &str) -> Result<bool, String> {
value.parse().map_err(|_| format!("failed to parse value `{}` for {}", value, name))
}
let mut config = BackendConfig {
codegen_mode: CodegenMode::Aot,
jit_args: match std::env::var("CG_CLIF_JIT_ARGS") {
Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(),
Err(std::env::VarError::NotPresent) => vec![],
Err(std::env::VarError::NotUnicode(s)) => {
panic!("CG_CLIF_JIT_ARGS not unicode: {:?}", s);
}
},
};
let mut config = BackendConfig::default();
for opt in opts {
if opt.starts_with("-import-instr-limit") {
// Silently ignore -import-instr-limit. It is set by rust's build system even when
@ -94,9 +45,14 @@ impl BackendConfig {
}
if let Some((name, value)) = opt.split_once('=') {
match name {
"mode" => config.codegen_mode = value.parse()?,
"enable_verifier" => config.enable_verifier = parse_bool(name, value)?,
"disable_incr_cache" => config.disable_incr_cache = parse_bool(name, value)?,
"mode" => {
config.codegen_mode = match value {
"aot" => CodegenMode::Aot,
"jit" => CodegenMode::Jit,
"jit-lazy" => CodegenMode::JitLazy,
_ => return Err(format!("Unknown codegen mode `{}`", value)),
};
}
_ => return Err(format!("Unknown option `{}`", name)),
}
} else {

View file

@ -452,8 +452,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
let data_id = match reloc_target_alloc {
GlobalAlloc::Function { instance, .. } => {
assert_eq!(addend, 0);
let func_id =
crate::abi::import_function(tcx, module, instance.polymorphize(tcx));
let func_id = crate::abi::import_function(tcx, module, instance);
let local_func_id = module.declare_func_in_data(func_id, &mut data);
data.write_function_addr(offset.bytes() as u32, local_func_id);
continue;

View file

@ -1,6 +1,7 @@
//! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a
//! standalone executable.
use std::env;
use std::fs::{self, File};
use std::io::BufWriter;
use std::path::{Path, PathBuf};
@ -25,13 +26,18 @@ use rustc_middle::mir::mono::{CodegenUnit, MonoItem};
use rustc_session::Session;
use rustc_session::config::{DebugInfo, OutFileName, OutputFilenames, OutputType};
use crate::BackendConfig;
use crate::CodegenCx;
use crate::base::CodegenedFunction;
use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken};
use crate::debuginfo::TypeDebugContext;
use crate::global_asm::GlobalAsmConfig;
use crate::prelude::*;
use crate::unwind_module::UnwindModule;
fn disable_incr_cache() -> bool {
env::var("CG_CLIF_DISABLE_INCR_CACHE").as_deref() == Ok("1")
}
struct ModuleCodegenResult {
module_regular: CompiledModule,
module_global_asm: Option<CompiledModule>,
@ -63,10 +69,10 @@ impl OngoingCodegen {
self,
sess: &Session,
outputs: &OutputFilenames,
backend_config: &BackendConfig,
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
let mut work_products = FxIndexMap::default();
let mut modules = vec![];
let disable_incr_cache = disable_incr_cache();
for module_codegen in self.modules {
let module_codegen_result = match module_codegen {
@ -87,7 +93,7 @@ impl OngoingCodegen {
if let Some((work_product_id, work_product)) = existing_work_product {
work_products.insert(work_product_id, work_product);
} else {
let work_product = if backend_config.disable_incr_cache {
let work_product = if disable_incr_cache {
None
} else if let Some(module_global_asm) = &module_global_asm {
rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
@ -322,12 +328,8 @@ fn produce_final_output_artifacts(
// These are used in linking steps and will be cleaned up afterward.
}
fn make_module(
sess: &Session,
backend_config: &BackendConfig,
name: String,
) -> UnwindModule<ObjectModule> {
let isa = crate::build_isa(sess, backend_config);
fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> {
let isa = crate::build_isa(sess);
let mut builder =
ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap();
@ -412,7 +414,13 @@ fn emit_module(
Err(err) => return Err(format!("error writing object file: {}", err)),
};
prof.artifact_size("object_file", &*name, file.metadata().unwrap().len());
if prof.enabled() {
prof.artifact_size(
"object_file",
tmp_file.file_name().unwrap().to_string_lossy(),
file.metadata().unwrap().len(),
);
}
Ok(CompiledModule {
name,
@ -486,91 +494,101 @@ fn reuse_workproduct_for_cgu(
})
}
fn codegen_cgu_content(
tcx: TyCtxt<'_>,
module: &mut dyn Module,
cgu_name: rustc_span::Symbol,
) -> (CodegenCx, Vec<CodegenedFunction>) {
let _timer = tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str());
let cgu = tcx.codegen_unit(cgu_name);
let mono_items = cgu.items_in_deterministic_order(tcx);
let mut cx = crate::CodegenCx::new(
tcx,
module.isa(),
tcx.sess.opts.debuginfo != DebugInfo::None,
cgu_name,
);
let mut type_dbg = TypeDebugContext::default();
super::predefine_mono_items(tcx, module, &mono_items);
let mut codegened_functions = vec![];
for (mono_item, _) in mono_items {
match mono_item {
MonoItem::Fn(inst) => {
if let Some(codegened_function) = crate::base::codegen_fn(
tcx,
&mut cx,
&mut type_dbg,
Function::new(),
module,
inst,
) {
codegened_functions.push(codegened_function);
}
}
MonoItem::Static(def_id) => {
let data_id = crate::constant::codegen_static(tcx, module, def_id);
if let Some(debug_context) = &mut cx.debug_context {
debug_context.define_static(tcx, &mut type_dbg, def_id, data_id);
}
}
MonoItem::GlobalAsm(item_id) => {
crate::global_asm::codegen_global_asm_item(tcx, &mut cx.global_asm, item_id);
}
}
}
crate::main_shim::maybe_create_entry_wrapper(tcx, module, false, cgu.is_primary());
(cx, codegened_functions)
}
fn module_codegen(
tcx: TyCtxt<'_>,
(backend_config, global_asm_config, cgu_name, token): (
BackendConfig,
(global_asm_config, cgu_name, token): (
Arc<GlobalAsmConfig>,
rustc_span::Symbol,
ConcurrencyLimiterToken,
),
) -> OngoingModuleCodegen {
let (cgu_name, mut cx, mut module, codegened_functions) =
tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str()).run(|| {
let cgu = tcx.codegen_unit(cgu_name);
let mono_items = cgu.items_in_deterministic_order(tcx);
let mut module = make_module(tcx.sess, cgu_name.as_str().to_string());
let mut module = make_module(tcx.sess, &backend_config, cgu_name.as_str().to_string());
let (mut cx, codegened_functions) = codegen_cgu_content(tcx, &mut module, cgu_name);
let mut cx = crate::CodegenCx::new(
tcx,
module.isa(),
tcx.sess.opts.debuginfo != DebugInfo::None,
cgu_name,
);
let mut type_dbg = TypeDebugContext::default();
super::predefine_mono_items(tcx, &mut module, &mono_items);
let mut codegened_functions = vec![];
for (mono_item, _) in mono_items {
match mono_item {
MonoItem::Fn(inst) => {
if let Some(codegened_function) = crate::base::codegen_fn(
tcx,
&mut cx,
&mut type_dbg,
Function::new(),
&mut module,
inst,
) {
codegened_functions.push(codegened_function);
}
}
MonoItem::Static(def_id) => {
let data_id = crate::constant::codegen_static(tcx, &mut module, def_id);
if let Some(debug_context) = &mut cx.debug_context {
debug_context.define_static(tcx, &mut type_dbg, def_id, data_id);
}
}
MonoItem::GlobalAsm(item_id) => {
crate::global_asm::codegen_global_asm_item(
tcx,
&mut cx.global_asm,
item_id,
);
}
}
}
crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, false, cgu.is_primary());
let cgu_name = cgu.name().as_str().to_owned();
(cgu_name, cx, module, codegened_functions)
});
let cgu_name = cgu_name.as_str().to_owned();
let producer = crate::debuginfo::producer(tcx.sess);
let profiler = tcx.prof.clone();
OngoingModuleCodegen::Async(std::thread::spawn(move || {
cx.profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| {
profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| {
cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler(
cx.profiler.clone(),
profiler.clone(),
)));
let mut cached_context = Context::new();
for codegened_func in codegened_functions {
crate::base::compile_fn(&mut cx, &mut cached_context, &mut module, codegened_func);
crate::base::compile_fn(
&mut cx,
&profiler,
&mut cached_context,
&mut module,
codegened_func,
);
}
});
let global_asm_object_file =
cx.profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| {
profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| {
crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, &cx.global_asm)
})?;
let codegen_result =
cx.profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| {
profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| {
emit_cgu(
&global_asm_config.output_filenames,
&cx.profiler,
&profiler,
cgu_name,
module,
cx.debug_context,
@ -583,9 +601,63 @@ fn module_codegen(
}))
}
fn emit_metadata_module(tcx: TyCtxt<'_>, metadata: &EncodedMetadata) -> CompiledModule {
use rustc_middle::mir::mono::CodegenUnitNameBuilder;
let _timer = tcx.sess.timer("write compressed metadata");
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
let metadata_cgu_name = cgu_name_builder
.build_cgu_name(LOCAL_CRATE, ["crate"], Some("metadata"))
.as_str()
.to_string();
let tmp_file =
tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name));
let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx);
let obj = create_compressed_metadata_file(tcx.sess, metadata, &symbol_name);
if let Err(err) = std::fs::write(&tmp_file, obj) {
tcx.dcx().fatal(format!("error writing metadata object file: {}", err));
}
CompiledModule {
name: metadata_cgu_name,
kind: ModuleKind::Metadata,
object: Some(tmp_file),
dwarf_object: None,
bytecode: None,
assembly: None,
llvm_ir: None,
}
}
fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option<CompiledModule> {
let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string());
let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module);
if created_alloc_shim {
let product = allocator_module.finish();
match emit_module(
tcx.output_filenames(()),
&tcx.sess.prof,
product.object,
ModuleKind::Allocator,
"allocator_shim".to_owned(),
&crate::debuginfo::producer(tcx.sess),
) {
Ok(allocator_module) => Some(allocator_module),
Err(err) => tcx.dcx().fatal(err),
}
} else {
None
}
}
pub(crate) fn run_aot(
tcx: TyCtxt<'_>,
backend_config: BackendConfig,
metadata: EncodedMetadata,
need_metadata_module: bool,
) -> Box<OngoingCodegen> {
@ -631,9 +703,10 @@ pub(crate) fn run_aot(
let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx));
let disable_incr_cache = disable_incr_cache();
let (todo_cgus, done_cgus) =
cgus.into_iter().enumerate().partition::<Vec<_>, _>(|&(i, _)| match cgu_reuse[i] {
_ if backend_config.disable_incr_cache => true,
_ if disable_incr_cache => true,
CguReuse::No => true,
CguReuse::PreLto | CguReuse::PostLto => false,
});
@ -647,12 +720,7 @@ pub(crate) fn run_aot(
.with_task(
dep_node,
tcx,
(
backend_config.clone(),
global_asm_config.clone(),
cgu.name(),
concurrency_limiter.acquire(tcx.dcx()),
),
(global_asm_config.clone(), cgu.name(), concurrency_limiter.acquire(tcx.dcx())),
module_codegen,
Some(rustc_middle::dep_graph::hash_result),
)
@ -666,62 +734,10 @@ pub(crate) fn run_aot(
modules
});
let mut allocator_module = make_module(tcx.sess, &backend_config, "allocator_shim".to_string());
let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module);
let allocator_module = emit_allocator_module(tcx);
let allocator_module = if created_alloc_shim {
let product = allocator_module.finish();
match emit_module(
tcx.output_filenames(()),
&tcx.sess.prof,
product.object,
ModuleKind::Allocator,
"allocator_shim".to_owned(),
&crate::debuginfo::producer(tcx.sess),
) {
Ok(allocator_module) => Some(allocator_module),
Err(err) => tcx.dcx().fatal(err),
}
} else {
None
};
let metadata_module = if need_metadata_module {
let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || {
use rustc_middle::mir::mono::CodegenUnitNameBuilder;
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
let metadata_cgu_name = cgu_name_builder
.build_cgu_name(LOCAL_CRATE, ["crate"], Some("metadata"))
.as_str()
.to_string();
let tmp_file =
tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name));
let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx);
let obj = create_compressed_metadata_file(tcx.sess, &metadata, &symbol_name);
if let Err(err) = std::fs::write(&tmp_file, obj) {
tcx.dcx().fatal(format!("error writing metadata object file: {}", err));
}
(metadata_cgu_name, tmp_file)
});
Some(CompiledModule {
name: metadata_cgu_name,
kind: ModuleKind::Metadata,
object: Some(tmp_file),
dwarf_object: None,
bytecode: None,
assembly: None,
llvm_ir: None,
})
} else {
None
};
let metadata_module =
if need_metadata_module { Some(emit_metadata_module(tcx, &metadata)) } else { None };
Box::new(OngoingCodegen {
modules,

View file

@ -11,12 +11,12 @@ use cranelift_jit::{JITBuilder, JITModule};
use rustc_codegen_ssa::CrateInfo;
use rustc_middle::mir::mono::MonoItem;
use rustc_session::Session;
use rustc_span::Symbol;
use rustc_span::sym;
use crate::debuginfo::TypeDebugContext;
use crate::prelude::*;
use crate::unwind_module::UnwindModule;
use crate::{BackendConfig, CodegenCx, CodegenMode};
use crate::{CodegenCx, CodegenMode};
struct JitState {
jit_module: UnwindModule<JITModule>,
@ -59,14 +59,10 @@ impl UnsafeMessage {
}
}
fn create_jit_module(
tcx: TyCtxt<'_>,
backend_config: &BackendConfig,
hotswap: bool,
) -> (UnwindModule<JITModule>, CodegenCx) {
fn create_jit_module(tcx: TyCtxt<'_>, hotswap: bool) -> (UnwindModule<JITModule>, CodegenCx) {
let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string());
let isa = crate::build_isa(tcx.sess, backend_config);
let isa = crate::build_isa(tcx.sess);
let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
jit_builder.hotswap(hotswap);
crate::compiler_builtins::register_functions_for_jit(&mut jit_builder);
@ -74,14 +70,14 @@ 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);
(jit_module, cx)
}
pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec<String>) -> ! {
if !tcx.sess.opts.output_types.should_codegen() {
tcx.dcx().fatal("JIT mode doesn't work with `cargo check`");
}
@ -90,11 +86,8 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
tcx.dcx().fatal("can't jit non-executable crate");
}
let (mut jit_module, mut cx) = create_jit_module(
tcx,
&backend_config,
matches!(backend_config.codegen_mode, CodegenMode::JitLazy),
);
let (mut jit_module, mut cx) =
create_jit_module(tcx, matches!(codegen_mode, CodegenMode::JitLazy));
let mut cached_context = Context::new();
let (_, cgus) = tcx.collect_and_partition_mono_items(());
@ -110,7 +103,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
super::predefine_mono_items(tcx, &mut jit_module, &mono_items);
for (mono_item, _) in mono_items {
match mono_item {
MonoItem::Fn(inst) => match backend_config.codegen_mode {
MonoItem::Fn(inst) => match codegen_mode {
CodegenMode::Aot => unreachable!(),
CodegenMode::Jit => {
codegen_and_compile_fn(
@ -151,7 +144,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
);
let args = std::iter::once(&*tcx.crate_name(LOCAL_CRATE).as_str().to_string())
.chain(backend_config.jit_args.iter().map(|arg| &**arg))
.chain(jit_args.iter().map(|arg| &**arg))
.map(|arg| CString::new(arg).unwrap())
.collect::<Vec<_>>();
@ -211,7 +204,7 @@ pub(crate) fn codegen_and_compile_fn<'tcx>(
instance: Instance<'tcx>,
) {
cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler(
cx.profiler.clone(),
tcx.prof.clone(),
)));
tcx.prof.generic_activity("codegen and compile fn").run(|| {
@ -227,7 +220,7 @@ pub(crate) fn codegen_and_compile_fn<'tcx>(
module,
instance,
) {
crate::base::compile_fn(cx, cached_context, module, codegened_func);
crate::base::compile_fn(cx, &tcx.prof, cached_context, module, codegened_func);
}
});
}
@ -276,12 +269,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

@ -102,13 +102,12 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>(
// Pass a wrapper rather than the function itself as the function itself may not
// be exported from the main codegen unit and may thus be unreachable from the
// object file created by an external assembler.
let inline_asm_index = fx.cx.inline_asm_index.get();
fx.cx.inline_asm_index.set(inline_asm_index + 1);
let wrapper_name = format!(
"__inline_asm_{}_wrapper_n{}",
fx.cx.cgu_name.as_str().replace('.', "__").replace('-', "_"),
inline_asm_index
fx.cx.inline_asm_index
);
fx.cx.inline_asm_index += 1;
let sig =
get_function_sig(fx.tcx, fx.target_config.default_call_conv, instance);
create_wrapper_function(fx.module, sig, &wrapper_name, symbol.name);
@ -167,13 +166,12 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>(
asm_gen.allocate_registers();
asm_gen.allocate_stack_slots();
let inline_asm_index = fx.cx.inline_asm_index.get();
fx.cx.inline_asm_index.set(inline_asm_index + 1);
let asm_name = format!(
"__inline_asm_{}_n{}",
fx.cx.cgu_name.as_str().replace('.', "__").replace('-', "_"),
inline_asm_index
fx.cx.inline_asm_index
);
fx.cx.inline_asm_index += 1;
let generated_asm = asm_gen.generate_asm_wrapper(&asm_name);
fx.cx.global_asm.push_str(&generated_asm);
@ -266,13 +264,12 @@ pub(crate) fn codegen_naked_asm<'tcx>(
// Pass a wrapper rather than the function itself as the function itself may not
// be exported from the main codegen unit and may thus be unreachable from the
// object file created by an external assembler.
let inline_asm_index = cx.inline_asm_index.get();
cx.inline_asm_index.set(inline_asm_index + 1);
let wrapper_name = format!(
"__inline_asm_{}_wrapper_n{}",
cx.cgu_name.as_str().replace('.', "__").replace('-', "_"),
inline_asm_index
cx.inline_asm_index
);
cx.inline_asm_index += 1;
let sig =
get_function_sig(tcx, module.target_config().default_call_conv, instance);
create_wrapper_function(module, sig, &wrapper_name, symbol.name);

View file

@ -1270,8 +1270,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
}
sym::cold_path => {
// This is a no-op. The intrinsic is just a hint to the optimizer.
// We still have an impl here to avoid it being turned into a call.
fx.bcx.set_cold_block(fx.bcx.current_block().unwrap());
}
// Unimplemented intrinsics must have a fallback body. The fallback body is obtained

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

@ -34,7 +34,7 @@ extern crate rustc_target;
extern crate rustc_driver;
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::env;
use std::sync::Arc;
use cranelift_codegen::isa::TargetIsa;
@ -42,7 +42,6 @@ use cranelift_codegen::settings::{self, Configurable};
use rustc_codegen_ssa::CodegenResults;
use rustc_codegen_ssa::back::versioned_llvm_target;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_session::Session;
@ -123,11 +122,10 @@ impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
/// The codegen context holds any information shared between the codegen of individual functions
/// inside a single codegen unit with the exception of the Cranelift [`Module`](cranelift_module::Module).
struct CodegenCx {
profiler: SelfProfilerRef,
output_filenames: Arc<OutputFilenames>,
should_write_ir: bool,
global_asm: String,
inline_asm_index: Cell<usize>,
inline_asm_index: usize,
debug_context: Option<DebugContext>,
cgu_name: Symbol,
}
@ -142,11 +140,10 @@ impl CodegenCx {
None
};
CodegenCx {
profiler: tcx.prof.clone(),
output_filenames: tcx.output_filenames(()).clone(),
should_write_ir: crate::pretty_clif::should_write_ir(tcx),
global_asm: String::new(),
inline_asm_index: Cell::new(0),
inline_asm_index: 0,
debug_context,
cgu_name,
}
@ -154,7 +151,7 @@ impl CodegenCx {
}
pub struct CraneliftCodegenBackend {
pub config: RefCell<Option<BackendConfig>>,
pub config: Option<BackendConfig>,
}
impl CodegenBackend for CraneliftCodegenBackend {
@ -176,31 +173,19 @@ impl CodegenBackend for CraneliftCodegenBackend {
sess.dcx()
.fatal("`-Cinstrument-coverage` is LLVM specific and not supported by Cranelift");
}
let mut config = self.config.borrow_mut();
if config.is_none() {
let new_config = BackendConfig::from_opts(&sess.opts.cg.llvm_args)
.unwrap_or_else(|err| sess.dcx().fatal(err));
*config = Some(new_config);
}
}
fn target_features(&self, sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> {
// 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],
}
@ -220,12 +205,15 @@ impl CodegenBackend for CraneliftCodegenBackend {
need_metadata_module: bool,
) -> Box<dyn Any> {
tcx.dcx().abort_if_errors();
let config = self.config.borrow().clone().unwrap();
let config = self.config.clone().unwrap_or_else(|| {
BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args)
.unwrap_or_else(|err| tcx.sess.dcx().fatal(err))
});
match config.codegen_mode {
CodegenMode::Aot => driver::aot::run_aot(tcx, config, metadata, need_metadata_module),
CodegenMode::Aot => driver::aot::run_aot(tcx, metadata, need_metadata_module),
CodegenMode::Jit | CodegenMode::JitLazy => {
#[cfg(feature = "jit")]
driver::jit::run_jit(tcx, config);
driver::jit::run_jit(tcx, config.codegen_mode, config.jit_args);
#[cfg(not(feature = "jit"))]
tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift");
@ -239,16 +227,20 @@ impl CodegenBackend for CraneliftCodegenBackend {
sess: &Session,
outputs: &OutputFilenames,
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
let _timer = sess.timer("finish_ongoing_codegen");
ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(
sess,
outputs,
self.config.borrow().as_ref().unwrap(),
)
ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(sess, outputs)
}
}
/// Determine if the Cranelift ir verifier should run.
///
/// Returns true when `-Zverify-llvm-ir` is passed, the `CG_CLIF_ENABLE_VERIFIER` env var is set to
/// 1 or when cg_clif is compiled with debug assertions enabled or false otherwise.
fn enable_verifier(sess: &Session) -> bool {
sess.verify_llvm_ir()
|| cfg!(debug_assertions)
|| env::var("CG_CLIF_ENABLE_VERIFIER").as_deref() == Ok("1")
}
fn target_triple(sess: &Session) -> target_lexicon::Triple {
// FIXME(madsmtm): Use `sess.target.llvm_target` once target-lexicon supports unversioned macOS.
// See <https://github.com/bytecodealliance/target-lexicon/pull/113>
@ -258,14 +250,14 @@ fn target_triple(sess: &Session) -> target_lexicon::Triple {
}
}
fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIsa + 'static> {
fn build_isa(sess: &Session) -> Arc<dyn TargetIsa + 'static> {
use target_lexicon::BinaryFormat;
let target_triple = crate::target_triple(sess);
let mut flags_builder = settings::builder();
flags_builder.enable("is_pic").unwrap();
let enable_verifier = if backend_config.enable_verifier { "true" } else { "false" };
let enable_verifier = if enable_verifier(sess) { "true" } else { "false" };
flags_builder.set("enable_verifier", enable_verifier).unwrap();
flags_builder.set("regalloc_checker", enable_verifier).unwrap();
@ -300,6 +292,16 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIs
}
}
if let target_lexicon::OperatingSystem::Windows = target_triple.operating_system {
// FIXME remove dependency on this from the Rust ABI. cc bytecodealliance/wasmtime#9510
flags_builder.enable("enable_multi_ret_implicit_sret").unwrap();
}
if let target_lexicon::Architecture::S390x = target_triple.architecture {
// FIXME remove dependency on this from the Rust ABI. cc bytecodealliance/wasmtime#9510
flags_builder.enable("enable_multi_ret_implicit_sret").unwrap();
}
if let target_lexicon::Architecture::Aarch64(_)
| target_lexicon::Architecture::Riscv64(_)
| target_lexicon::Architecture::X86_64 = target_triple.architecture
@ -352,5 +354,5 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn TargetIs
/// This is the entrypoint for a hot plugged rustc_codegen_cranelift
#[no_mangle]
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
Box::new(CraneliftCodegenBackend { config: RefCell::new(None) })
Box::new(CraneliftCodegenBackend { config: None })
}

View file

@ -24,7 +24,7 @@ pub(crate) fn maybe_create_entry_wrapper(
};
if main_def_id.is_local() {
let instance = Instance::mono(tcx, main_def_id).polymorphize(tcx);
let instance = Instance::mono(tcx, main_def_id);
if module.get_name(tcx.symbol_name(instance).name).is_none() {
return;
}
@ -75,7 +75,7 @@ pub(crate) fn maybe_create_entry_wrapper(
}
};
let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx);
let instance = Instance::mono(tcx, rust_main_def_id);
let main_name = tcx.symbol_name(instance).name;
let main_sig = get_function_sig(tcx, m.target_config().default_call_conv, instance);
@ -117,8 +117,7 @@ pub(crate) fn maybe_create_entry_wrapper(
report.def_id,
tcx.mk_args(&[GenericArg::from(main_ret_ty)]),
DUMMY_SP,
)
.polymorphize(tcx);
);
let report_name = tcx.symbol_name(report).name;
let report_sig = get_function_sig(tcx, m.target_config().default_call_conv, report);
@ -143,8 +142,7 @@ pub(crate) fn maybe_create_entry_wrapper(
start_def_id,
tcx.mk_args(&[main_ret_ty.into()]),
DUMMY_SP,
)
.polymorphize(tcx);
);
let start_func_id = import_function(tcx, m, start_instance);
let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref);

View file

@ -1005,9 +1005,6 @@ pub(crate) fn assert_assignable<'tcx>(
}
}
}
(ty::Param(_), _) | (_, ty::Param(_)) if fx.tcx.sess.opts.unstable_opts.polymorphize => {
// No way to check if it is correct or not with polymorphization enabled
}
_ => {
assert_eq!(
from_ty,

View file

@ -52,24 +52,24 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {}
#[lang = "copy"]
pub unsafe trait Copy {}
pub trait Copy {}
unsafe impl Copy for bool {}
unsafe impl Copy for u8 {}
unsafe impl Copy for u16 {}
unsafe impl Copy for u32 {}
unsafe impl Copy for u64 {}
unsafe impl Copy for usize {}
unsafe impl Copy for i8 {}
unsafe impl Copy for i16 {}
unsafe impl Copy for i32 {}
unsafe impl Copy for isize {}
unsafe impl Copy for f32 {}
unsafe impl Copy for f64 {}
unsafe impl Copy for char {}
unsafe impl<'a, T: ?Sized> Copy for &'a T {}
unsafe impl<T: ?Sized> Copy for *const T {}
unsafe impl<T: ?Sized> Copy for *mut T {}
impl Copy for bool {}
impl Copy for u8 {}
impl Copy for u16 {}
impl Copy for u32 {}
impl Copy for u64 {}
impl Copy for usize {}
impl Copy for i8 {}
impl Copy for i16 {}
impl Copy for i32 {}
impl Copy for isize {}
impl Copy for f32 {}
impl Copy for f64 {}
impl Copy for char {}
impl<'a, T: ?Sized> Copy for &'a T {}
impl<T: ?Sized> Copy for *const T {}
impl<T: ?Sized> Copy for *mut T {}
#[lang = "sync"]
pub unsafe trait Sync {}

View file

@ -656,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",
@ -736,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

@ -544,7 +544,10 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> {
impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> {
fn x86_abi_opt(&self) -> X86Abi {
X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm }
X86Abi {
regparm: self.tcx.sess.opts.unstable_opts.regparm,
reg_struct_return: self.tcx.sess.opts.unstable_opts.reg_struct_return,
}
}
}

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

@ -6,7 +6,6 @@ tests/ui/functions-closures/parallel-codegen-closures.rs
tests/ui/linkage-attr/linkage1.rs
tests/ui/lto/dylib-works.rs
tests/ui/numbers-arithmetic/saturating-float-casts.rs
tests/ui/polymorphization/promoted-function.rs
tests/ui/sepcomp/sepcomp-cci.rs
tests/ui/sepcomp/sepcomp-extern.rs
tests/ui/sepcomp/sepcomp-fns-backwards.rs

View file

@ -656,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",
@ -825,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(),
@ -1042,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,
}
}
@ -1177,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,
}
}
@ -1301,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

@ -302,10 +302,9 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
(value, AddressSpace::DATA)
}
}
GlobalAlloc::Function { instance, .. } => (
self.get_fn_addr(instance.polymorphize(self.tcx)),
self.data_layout().instruction_address_space,
),
GlobalAlloc::Function { instance, .. } => {
(self.get_fn_addr(instance), self.data_layout().instruction_address_space)
}
GlobalAlloc::VTable(ty, dyn_ty) => {
let alloc = self
.tcx

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>>,
@ -159,6 +159,11 @@ pub(crate) unsafe fn create_module<'ll>(
// See https://github.com/llvm/llvm-project/pull/112084
target_data_layout = target_data_layout.replace("-i128:128", "");
}
if sess.target.arch.starts_with("powerpc64") {
// LLVM 20 updates the powerpc64 layout to correctly align 128 bit integers to 128 bit.
// See https://github.com/llvm/llvm-project/pull/118004
target_data_layout = target_data_layout.replace("-i128:128", "");
}
}
// Ensure the data-layout values hardcoded remain the defaults.
@ -525,7 +530,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 +581,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,201 +1,38 @@
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexSet;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::coverage::{
CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, MappingKind, Op,
CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op,
SourceRegion,
};
use rustc_middle::ty::Instance;
use rustc_span::Span;
use tracing::{debug, instrument};
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
/// Holds all of the coverage mapping data associated with a function instance,
/// collected during traversal of `Coverage` statements in the function's MIR.
#[derive(Debug)]
pub(crate) struct FunctionCoverageCollector<'tcx> {
/// Coverage info that was attached to this function by the instrumentor.
function_coverage_info: &'tcx FunctionCoverageInfo,
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> {
/// Creates a new set of coverage data for a used (called) function.
pub(crate) fn new(
instance: Instance<'tcx>,
function_coverage_info: &'tcx FunctionCoverageInfo,
) -> Self {
Self::create(instance, function_coverage_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,
) -> Self {
Self::create(instance, function_coverage_info, false)
}
fn create(
instance: Instance<'tcx>,
function_coverage_info: &'tcx FunctionCoverageInfo,
is_used: bool,
) -> Self {
let num_counters = function_coverage_info.num_counters;
let num_expressions = function_coverage_info.expressions.len();
debug!(
"FunctionCoverage::create(instance={instance:?}) has \
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);
}
/// Identify expressions that will always have a value of zero, and note
/// their IDs in [`ZeroExpressions`]. Mappings that refer to a zero expression
/// can instead become mappings to a constant zero value.
///
/// This method mainly exists to preserve the simplifications that were
/// already being performed by the Rust-side expression renumbering, so that
/// the resulting coverage mappings don't get worse.
fn identify_zero_expressions(&self) -> ZeroExpressions {
// The set of expressions that either were optimized out entirely, or
// have zero as both of their operands, and will therefore always have
// a value of zero. Other expressions that refer to these as operands
// can have those operands replaced with `CovTerm::Zero`.
let mut zero_expressions = ZeroExpressions::default();
// Simplify a copy of each expression based on lower-numbered expressions,
// and then update the set of always-zero expressions if necessary.
// (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 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);
continue;
}
// We don't need to simplify the actual expression data in the
// expressions list; we can just simplify a temporary copy and then
// use that to update the set of always-zero expressions.
let Expression { mut lhs, op, mut rhs } = *expression;
// If an expression has an operand that is also an expression, the
// operand's ID must be strictly lower. This is what lets us find
// all zero expressions in one pass.
let assert_operand_expression_is_lower = |operand_id: ExpressionId| {
assert!(
operand_id < id,
"Operand {operand_id:?} should be less than {id:?} in {expression:?}",
)
};
// If an operand refers to a counter or expression that is always
// zero, then that operand can be replaced with `CovTerm::Zero`.
let maybe_set_operand_to_zero = |operand: &mut CovTerm| {
if let CovTerm::Expression(id) = *operand {
assert_operand_expression_is_lower(id);
}
if is_zero_term(&self.counters_seen, &zero_expressions, *operand) {
*operand = CovTerm::Zero;
}
};
maybe_set_operand_to_zero(&mut lhs);
maybe_set_operand_to_zero(&mut rhs);
// Coverage counter values cannot be negative, so if an expression
// involves subtraction from zero, assume that its RHS must also be zero.
// (Do this after simplifications that could set the LHS to zero.)
if lhs == CovTerm::Zero && op == Op::Subtract {
rhs = CovTerm::Zero;
}
// After the above simplifications, if both operands are zero, then
// we know that this expression is always zero too.
if lhs == CovTerm::Zero && rhs == CovTerm::Zero {
zero_expressions.insert(id);
}
}
zero_expressions
}
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;
FunctionCoverage { function_coverage_info, is_used, counters_seen, zero_expressions }
}
}
pub(crate) struct FunctionCoverage<'tcx> {
pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo,
is_used: bool,
counters_seen: BitSet<CounterId>,
zero_expressions: ZeroExpressions,
/// If `None`, the corresponding function is unused.
ids_info: Option<&'tcx CoverageIdsInfo>,
}
impl<'tcx> FunctionCoverage<'tcx> {
pub(crate) fn new_used(
function_coverage_info: &'tcx FunctionCoverageInfo,
ids_info: &'tcx CoverageIdsInfo,
) -> Self {
Self { function_coverage_info, ids_info: Some(ids_info) }
}
pub(crate) fn new_unused(function_coverage_info: &'tcx FunctionCoverageInfo) -> Self {
Self { function_coverage_info, ids_info: None }
}
/// Returns true for a used (called) function, and false for an unused function.
pub(crate) fn is_used(&self) -> bool {
self.is_used
self.ids_info.is_some()
}
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
/// or not the source code structure changed between different compilations.
pub(crate) fn source_hash(&self) -> u64 {
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
if self.is_used() { self.function_coverage_info.function_source_hash } else { 0 }
}
/// Convert this function's coverage expression data into a form that can be
@ -220,16 +57,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,37 +75,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
}
fn is_zero_term(&self, term: CovTerm) -> bool {
is_zero_term(&self.counters_seen, &self.zero_expressions, term)
}
}
/// Set of expression IDs that are known to always evaluate to zero.
/// Any mapping or expression operand that refers to these expressions can have
/// that reference replaced with a constant zero value.
#[derive(Default)]
struct ZeroExpressions(FxIndexSet<ExpressionId>);
impl ZeroExpressions {
fn insert(&mut self, id: ExpressionId) {
self.0.insert(id);
}
fn contains(&self, id: ExpressionId) -> bool {
self.0.contains(&id)
}
}
/// Returns `true` if the given term is known to have a value of zero, taking
/// into account knowledge of which counters are unused and which expressions
/// are always zero.
fn is_zero_term(
counters_seen: &BitSet<CounterId>,
zero_expressions: &ZeroExpressions,
term: CovTerm,
) -> bool {
match term {
CovTerm::Zero => true,
CovTerm::Counter(id) => !counters_seen.contains(id),
CovTerm::Expression(id) => zero_expressions.contains(id),
match self.ids_info {
Some(ids_info) => ids_info.is_zero_term(term),
// This function is unused, so all coverage counters/expressions are zero.
None => true,
}
}
}

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,12 +15,12 @@ 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;
use crate::common::CodegenCx;
use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector};
use crate::coverageinfo::map_data::FunctionCoverage;
use crate::coverageinfo::{ffi, llvm_cov};
use crate::llvm;
@ -65,20 +63,15 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
None => return,
};
if function_coverage_map.is_empty() {
// This module has no functions with coverage instrumentation
// This CGU has no functions with coverage instrumentation.
return;
}
let function_coverage_entries = function_coverage_map
.into_iter()
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
.collect::<Vec<_>>();
let all_files = function_coverage_entries
let all_file_names = function_coverage_map
.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);
@ -94,7 +87,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
let mut unused_function_names = Vec::new();
// Encode coverage mappings and generate function records
for (instance, function_coverage) in function_coverage_entries {
for (instance, function_coverage) in function_coverage_map {
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
let mangled_function_name = tcx.symbol_name(instance).name;
@ -105,8 +98,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 +143,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 +173,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 +203,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 +229,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 +246,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 +260,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 +295,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 +530,7 @@ 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 = FunctionCoverage::new_unused(function_coverage_info);
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

@ -13,7 +13,7 @@ use tracing::{debug, instrument};
use crate::builder::Builder;
use crate::common::CodegenCx;
use crate::coverageinfo::map_data::FunctionCoverageCollector;
use crate::coverageinfo::map_data::FunctionCoverage;
use crate::llvm;
pub(crate) mod ffi;
@ -21,18 +21,17 @@ 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>>>,
pub(crate) function_coverage_map: RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
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(),
@ -42,9 +41,7 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
}
}
fn take_function_coverage_map(
&self,
) -> FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> {
fn take_function_coverage_map(&self) -> FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>> {
self.function_coverage_map.replace(FxIndexMap::default())
}
@ -143,6 +140,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 +154,27 @@ 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(|| {
FunctionCoverage::new_used(
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 +191,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,
@ -467,8 +471,6 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) ->
AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id),
},
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
// Type parameters from polymorphized functions.
ty::Param(_) => build_param_type_di_node(cx, t),
_ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t),
};
@ -722,6 +724,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 +739,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 +762,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 +859,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,
@ -848,26 +869,6 @@ fn build_foreign_type_di_node<'ll, 'tcx>(
)
}
fn build_param_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
t: Ty<'tcx>,
) -> DINodeCreationResult<'ll> {
debug!("build_param_type_di_node: {:?}", t);
let name = format!("{t:?}");
DINodeCreationResult {
di_node: unsafe {
llvm::LLVMRustDIBuilderCreateBasicType(
DIB(cx),
name.as_c_char_ptr(),
name.len(),
Size::ZERO.bits(),
DW_ATE_unsigned,
)
},
already_stored_in_typemap: false,
}
}
pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
tcx: TyCtxt<'tcx>,
codegen_unit_name: &str,
@ -989,15 +990,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 +1049,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 +1062,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 +1082,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 +1095,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 +1145,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 +1171,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 +1190,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 +1212,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 +1225,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 +1249,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 +1262,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 +1275,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 +1288,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 +1363,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 +1453,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 +1491,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
field_offset,
DIFlags::FlagZero,
field_type_di_node,
None,
))
})
.collect()
@ -1606,3 +1643,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();

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