Auto merge of #3038 - rust-lang:rustup-2023-08-26, r=RalfJung

Automatic sync from rustc
This commit is contained in:
bors 2023-08-26 06:09:32 +00:00
commit adb16d5ebd
611 changed files with 8936 additions and 3431 deletions

View file

@ -2,15 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
dependencies = [
"gimli 0.27.3",
]
[[package]]
name = "addr2line"
version = "0.21.0"
@ -18,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"compiler_builtins",
"gimli 0.28.0",
"gimli",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
@ -180,7 +171,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9792d37ca5173d7e7f4fe453739a0671d0557915a030a383d6b866476bbc3e71"
dependencies = [
"object 0.32.0",
"object",
]
[[package]]
@ -246,16 +237,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.68"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line 0.20.0",
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object 0.31.1",
"object",
"rustc-demangle",
]
@ -1435,12 +1426,6 @@ dependencies = [
"wasi",
]
[[package]]
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]]
name = "gimli"
version = "0.28.0"
@ -2430,15 +2415,6 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
dependencies = [
"memchr",
]
[[package]]
name = "object"
version = "0.32.0"
@ -3357,7 +3333,7 @@ dependencies = [
"cstr",
"libc",
"measureme",
"object 0.32.0",
"object",
"rustc-demangle",
"rustc_ast",
"rustc_attr",
@ -3393,7 +3369,7 @@ dependencies = [
"itertools",
"jobserver",
"libc",
"object 0.32.0",
"object",
"pathdiff",
"regex",
"rustc_arena",
@ -4327,7 +4303,7 @@ name = "rustc_target"
version = "0.0.0"
dependencies = [
"bitflags 1.3.2",
"object 0.32.0",
"object",
"rustc_abi",
"rustc_data_structures",
"rustc_feature",
@ -4856,7 +4832,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
name = "std"
version = "0.0.0"
dependencies = [
"addr2line 0.21.0",
"addr2line",
"alloc",
"cfg-if",
"compiler_builtins",
@ -4867,7 +4843,7 @@ dependencies = [
"hermit-abi 0.3.2",
"libc",
"miniz_oxide",
"object 0.32.0",
"object",
"panic_abort",
"panic_unwind",
"profiler_builtins",
@ -5171,9 +5147,9 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4db52ee8fec06e119b692ef3dd2c4cf621a99204c1b8c47407870ed050305b9b"
dependencies = [
"gimli 0.28.0",
"gimli",
"hashbrown 0.14.0",
"object 0.32.0",
"object",
"tracing",
]

View file

@ -65,16 +65,16 @@ Rustdoc
Stabilized APIs
---------------
- [`impl<T: Send> Sync for mpsc::Sender<T>`](https://doc.rust-lang.org/nightly/std/sync/mpsc/struct.Sender.html#impl-Sync-for-Sender%3CT%3E)
- [`impl TryFrom<&OsStr> for &str`](https://doc.rust-lang.org/nightly/std/primitive.str.html#impl-TryFrom%3C%26'a+OsStr%3E-for-%26'a+str)
- [`String::leak`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.leak)
- [`impl<T: Send> Sync for mpsc::Sender<T>`](https://doc.rust-lang.org/stable/std/sync/mpsc/struct.Sender.html#impl-Sync-for-Sender%3CT%3E)
- [`impl TryFrom<&OsStr> for &str`](https://doc.rust-lang.org/stable/std/primitive.str.html#impl-TryFrom%3C%26'a+OsStr%3E-for-%26'a+str)
- [`String::leak`](https://doc.rust-lang.org/stable/alloc/string/struct.String.html#method.leak)
These APIs are now stable in const contexts:
- [`CStr::from_bytes_with_nul`](https://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
- [`CStr::to_bytes`](https://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
- [`CStr::to_bytes_with_nul`](https://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
- [`CStr::to_str`](https://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
- [`CStr::from_bytes_with_nul`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
- [`CStr::to_bytes`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.to_bytes)
- [`CStr::to_bytes_with_nul`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.to_bytes_with_nul)
- [`CStr::to_str`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.to_str)
<a id="1.72.0-Cargo"></a>

View file

@ -1,5 +1,5 @@
#![cfg_attr(feature = "nightly", feature(step_trait, rustc_attrs, min_specialization))]
#![cfg_attr(all(not(bootstrap), feature = "nightly"), allow(internal_features))]
#![cfg_attr(feature = "nightly", allow(internal_features))]
use std::fmt;
#[cfg(feature = "nightly")]

View file

@ -24,7 +24,7 @@
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#![allow(clippy::mut_from_ref)] // Arena allocators are one of the places where this pattern is fine.
use smallvec::SmallVec;

View file

@ -2092,6 +2092,10 @@ pub enum TyKind {
Never,
/// A tuple (`(A, B, C, D,...)`).
Tup(ThinVec<P<Ty>>),
/// An anonymous struct type i.e. `struct { foo: Type }`
AnonStruct(ThinVec<FieldDef>),
/// An anonymous union type i.e. `union { bar: Type }`
AnonUnion(ThinVec<FieldDef>),
/// A path (`module::module::...::Type`), optionally
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
///

View file

@ -510,6 +510,9 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
}
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
TyKind::AnonStruct(fields) | TyKind::AnonUnion(fields) => {
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
}
}
vis.visit_span(span);
visit_lazy_tts(tokens, vis);

View file

@ -486,6 +486,8 @@ impl Token {
Lt | BinOp(Shl) | // associated path
ModSep => true, // global path
Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
// For anonymous structs or unions, which only appear in specific positions
// (type of struct fields or union fields), we don't consider them as regular types
_ => false,
}
}

View file

@ -438,6 +438,9 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
TyKind::MacCall(mac) => visitor.visit_mac_call(mac),
TyKind::Never | TyKind::CVarArgs => {}
TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
walk_list!(visitor, visit_field_def, fields)
}
}
}

View file

@ -1293,6 +1293,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
TyKind::Err => {
hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered"))
}
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
TyKind::AnonStruct(ref _fields) => hir::TyKind::Err(
self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"),
),
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
TyKind::AnonUnion(ref _fields) => hir::TyKind::Err(
self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"),
),
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
TyKind::Ref(region, mt) => {

View file

@ -1,3 +1,7 @@
ast_passes_anon_struct_or_union_not_allowed =
anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields
.label = anonymous {$struct_or_union} declared here
ast_passes_assoc_const_without_body =
associated constant in `impl` without body
.suggestion = provide a definition for the constant
@ -162,6 +166,14 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
ast_passes_invalid_label =
invalid label name `{$name}`
ast_passes_invalid_unnamed_field =
unnamed fields are not allowed outside of structs or unions
.label = unnamed field declared here
ast_passes_invalid_unnamed_field_ty =
unnamed fields can only have struct or union types
.label = not a struct or union
ast_passes_item_underscore = `{$kind}` items in this context need a name
.label = `_` is not a valid name for this `{$kind}` item

View file

@ -223,10 +223,27 @@ impl<'a> AstValidator<'a> {
}
}
}
TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
walk_list!(self, visit_field_def, fields)
}
_ => visit::walk_ty(self, t),
}
}
fn visit_struct_field_def(&mut self, field: &'a FieldDef) {
if let Some(ident) = field.ident &&
ident.name == kw::Underscore {
self.check_unnamed_field_ty(&field.ty, ident.span);
self.visit_vis(&field.vis);
self.visit_ident(ident);
self.visit_ty_common(&field.ty);
self.walk_ty(&field.ty);
walk_list!(self, visit_attribute, &field.attrs);
} else {
self.visit_field_def(field);
}
}
fn err_handler(&self) -> &rustc_errors::Handler {
&self.session.diagnostic()
}
@ -264,6 +281,42 @@ impl<'a> AstValidator<'a> {
}
}
fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) {
if matches!(
&ty.kind,
// We already checked for `kw::Underscore` before calling this function,
// so skip the check
TyKind::AnonStruct(..) | TyKind::AnonUnion(..)
// If the anonymous field contains a Path as type, we can't determine
// if the path is a valid struct or union, so skip the check
| TyKind::Path(..)
) {
return;
}
self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
}
fn deny_anon_struct_or_union(&self, ty: &Ty) {
let struct_or_union = match &ty.kind {
TyKind::AnonStruct(..) => "struct",
TyKind::AnonUnion(..) => "union",
_ => return,
};
self.err_handler()
.emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
}
fn deny_unnamed_field(&self, field: &FieldDef) {
if let Some(ident) = field.ident &&
ident.name == kw::Underscore {
self.err_handler()
.emit_err(errors::InvalidUnnamedField {
span: field.span,
ident_span: ident.span
});
}
}
fn check_trait_fn_not_const(&self, constness: Const) {
if let Const::Yes(span) = constness {
self.session.emit_err(errors::TraitFnConst { span });
@ -789,6 +842,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_ty(&mut self, ty: &'a Ty) {
self.visit_ty_common(ty);
self.deny_anon_struct_or_union(ty);
self.walk_ty(ty)
}
@ -803,6 +857,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
fn visit_field_def(&mut self, field: &'a FieldDef) {
self.deny_unnamed_field(field);
visit::walk_field_def(self, field)
}
@ -995,10 +1050,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_mod_file_item_asciionly(item.ident);
}
}
ItemKind::Union(vdata, ..) => {
ItemKind::Struct(vdata, generics) => match vdata {
// Duplicating the `Visitor` logic allows catching all cases
// of `Anonymous(Struct, Union)` outside of a field struct or union.
//
// Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
// encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
// it uses `visit_ty_common`, which doesn't contain that specific check.
VariantData::Struct(fields, ..) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.visit_generics(generics);
walk_list!(self, visit_struct_field_def, fields);
walk_list!(self, visit_attribute, &item.attrs);
return;
}
_ => {}
},
ItemKind::Union(vdata, generics) => {
if vdata.fields().is_empty() {
self.err_handler().emit_err(errors::FieldlessUnion { span: item.span });
}
match vdata {
VariantData::Struct(fields, ..) => {
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.visit_generics(generics);
walk_list!(self, visit_struct_field_def, fields);
walk_list!(self, visit_attribute, &item.attrs);
return;
}
_ => {}
}
}
ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => {
self.check_defaultness(item.span, *defaultness);

View file

@ -727,3 +727,30 @@ pub struct ConstraintOnNegativeBound {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_invalid_unnamed_field_ty)]
pub struct InvalidUnnamedFieldTy {
#[primary_span]
pub span: Span,
#[label]
pub ty_span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_invalid_unnamed_field)]
pub struct InvalidUnnamedField {
#[primary_span]
pub span: Span,
#[label]
pub ident_span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_anon_struct_or_union_not_allowed)]
pub struct AnonStructOrUnionNotAllowed {
#[primary_span]
#[label]
pub span: Span,
pub struct_or_union: &'static str,
}

View file

@ -570,6 +570,7 @@ 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!(unnamed_fields, "unnamed fields are not yet fully implemented");
if !visitor.features.negative_bounds {
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {

View file

@ -1064,6 +1064,14 @@ impl<'a> State<'a> {
}
self.pclose();
}
ast::TyKind::AnonStruct(fields) => {
self.head("struct");
self.print_record_struct_body(&fields, ty.span);
}
ast::TyKind::AnonUnion(fields) => {
self.head("union");
self.print_record_struct_body(&fields, ty.span);
}
ast::TyKind::Paren(typ) => {
self.popen();
self.print_type(typ);

View file

@ -443,7 +443,11 @@ impl<'a> State<'a> {
}
}
fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) {
pub(crate) fn print_record_struct_body(
&mut self,
fields: &[ast::FieldDef],
span: rustc_span::Span,
) {
self.nbsp();
self.bopen();

View file

@ -937,6 +937,7 @@ pub fn find_deprecation(
#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
pub enum ReprAttr {
ReprInt(IntType),
ReprRust,
ReprC,
ReprPacked(u32),
ReprSimd,
@ -985,6 +986,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
let mut recognised = false;
if item.is_word() {
let hint = match item.name_or_empty() {
sym::Rust => Some(ReprRust),
sym::C => Some(ReprC),
sym::packed => Some(ReprPacked(1)),
sym::simd => Some(ReprSimd),

View file

@ -225,17 +225,17 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
if suggest {
borrow_spans.var_subdiag(
None,
&mut err,
Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
|_kind, var_span| {
let place = self.describe_any_place(access_place.as_ref());
crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
place,
var_span,
}
},
);
None,
&mut err,
Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
|_kind, var_span| {
let place = self.describe_any_place(access_place.as_ref());
crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
place,
var_span,
}
},
);
}
borrow_span
}
@ -262,11 +262,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} => {
err.span_label(span, format!("cannot {act}"));
if let Some(span) = get_mut_span_in_struct_field(
self.infcx.tcx,
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty,
*field,
) {
let place = Place::ty_from(local, proj_base, self.body, self.infcx.tcx);
if let Some(span) = get_mut_span_in_struct_field(self.infcx.tcx, place.ty, *field) {
err.span_suggestion_verbose(
span,
"consider changing this to be mutable",
@ -781,83 +778,88 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// Attempt to search similar mutable associated items for suggestion.
// In the future, attempt in all path but initially for RHS of for_loop
fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic) {
fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic, span: Span) {
use hir::{
Expr,
ExprKind::{Block, Call, DropTemps, Match, MethodCall},
BorrowKind, Expr,
ExprKind::{AddrOf, Block, Call, MethodCall},
};
let hir_map = self.infcx.tcx.hir();
if let Some(body_id) = hir_map.maybe_body_owned_by(self.mir_def_id()) {
if let Block(
hir::Block {
expr:
Some(Expr {
kind:
DropTemps(Expr {
kind:
Match(
Expr {
kind:
Call(
_,
[
Expr {
kind:
MethodCall(path_segment, _, _, span),
hir_id,
..
},
..,
],
),
..
},
..,
),
..
}),
..
}),
..
},
_,
) = hir_map.body(body_id).value.kind
{
let opt_suggestions = self
.infcx
.tcx
.typeck(path_segment.hir_id.owner.def_id)
.type_dependent_def_id(*hir_id)
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
.map(|def_id| self.infcx.tcx.associated_items(def_id))
.map(|assoc_items| {
assoc_items
.in_definition_order()
.map(|assoc_item_def| assoc_item_def.ident(self.infcx.tcx))
.filter(|&ident| {
let original_method_ident = path_segment.ident;
original_method_ident != ident
&& ident
.as_str()
.starts_with(&original_method_ident.name.to_string())
})
.map(|ident| format!("{ident}()"))
.peekable()
});
struct Finder<'tcx> {
span: Span,
expr: Option<&'tcx Expr<'tcx>>,
}
if let Some(mut suggestions) = opt_suggestions
&& suggestions.peek().is_some()
{
err.span_suggestions(
*span,
"use mutable method",
suggestions,
Applicability::MaybeIncorrect,
);
impl<'tcx> Visitor<'tcx> for Finder<'tcx> {
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
if e.span == self.span && self.expr.is_none() {
self.expr = Some(e);
}
hir::intravisit::walk_expr(self, e);
}
}
if let Some(body_id) = hir_map.maybe_body_owned_by(self.mir_def_id())
&& let Block(block, _) = hir_map.body(body_id).value.kind
{
// `span` corresponds to the expression being iterated, find the `for`-loop desugared
// expression with that span in order to identify potential fixes when encountering a
// read-only iterator that should be mutable.
let mut v = Finder {
span,
expr: None,
};
v.visit_block(block);
if let Some(expr) = v.expr && let Call(_, [expr]) = expr.kind {
match expr.kind {
MethodCall(path_segment, _, _, span) => {
// We have `for _ in iter.read_only_iter()`, try to
// suggest `for _ in iter.mutable_iter()` instead.
let opt_suggestions = self
.infcx
.tcx
.typeck(path_segment.hir_id.owner.def_id)
.type_dependent_def_id(expr.hir_id)
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
.map(|def_id| self.infcx.tcx.associated_items(def_id))
.map(|assoc_items| {
assoc_items
.in_definition_order()
.map(|assoc_item_def| assoc_item_def.ident(self.infcx.tcx))
.filter(|&ident| {
let original_method_ident = path_segment.ident;
original_method_ident != ident
&& ident.as_str().starts_with(
&original_method_ident.name.to_string(),
)
})
.map(|ident| format!("{ident}()"))
.peekable()
});
if let Some(mut suggestions) = opt_suggestions
&& suggestions.peek().is_some()
{
err.span_suggestions(
span,
"use mutable method",
suggestions,
Applicability::MaybeIncorrect,
);
}
}
AddrOf(BorrowKind::Ref, Mutability::Not, expr) => {
// We have `for _ in &i`, suggest `for _ in &mut i`.
err.span_suggestion_verbose(
expr.span.shrink_to_lo(),
"use a mutable iterator instead",
"mut ".to_string(),
Applicability::MachineApplicable,
);
}
_ => {}
}
}
};
}
}
/// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
@ -1003,9 +1005,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
// on for loops, RHS points to the iterator part
Some(DesugaringKind::ForLoop) => {
self.suggest_similar_mut_method_for_for_loop(err);
let span = opt_assignment_rhs_span.unwrap();
self.suggest_similar_mut_method_for_for_loop(err, span);
err.span_label(
opt_assignment_rhs_span.unwrap(),
span,
format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
);
None

View file

@ -202,7 +202,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
}
}
TerminatorKind::Goto { target: _ }
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Unreachable
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {

View file

@ -11,7 +11,7 @@
#![feature(trusted_step)]
#![feature(try_blocks)]
#![recursion_limit = "256"]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#[macro_use]
extern crate rustc_middle;
@ -770,7 +770,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}
TerminatorKind::Goto { target: _ }
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Unreachable
| TerminatorKind::UnwindResume
| TerminatorKind::Return
@ -817,7 +817,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}
}
TerminatorKind::UnwindTerminate
TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Assert { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::Drop { .. }

View file

@ -1334,7 +1334,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
match &term.kind {
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable
@ -1613,7 +1613,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
span_mirbug!(self, block_data, "resume on non-cleanup block!")
}
}
TerminatorKind::UnwindTerminate => {
TerminatorKind::UnwindTerminate(_) => {
if !is_cleanup {
span_mirbug!(self, block_data, "abort on non-cleanup block!")
}
@ -1697,7 +1697,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
span_mirbug!(self, ctxt, "unwind on cleanup block")
}
}
UnwindAction::Unreachable | UnwindAction::Terminate => (),
UnwindAction::Unreachable | UnwindAction::Terminate(_) => (),
}
}

View file

@ -474,8 +474,8 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
*destination,
);
}
TerminatorKind::UnwindTerminate => {
codegen_panic_cannot_unwind(fx, source_info);
TerminatorKind::UnwindTerminate(reason) => {
codegen_unwind_terminate(fx, source_info, *reason);
}
TerminatorKind::UnwindResume => {
// FIXME implement unwinding
@ -971,13 +971,14 @@ pub(crate) fn codegen_panic_nounwind<'tcx>(
codegen_panic_inner(fx, rustc_hir::LangItem::PanicNounwind, &args, source_info.span);
}
pub(crate) fn codegen_panic_cannot_unwind<'tcx>(
pub(crate) fn codegen_unwind_terminate<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
source_info: mir::SourceInfo,
reason: UnwindTerminateReason,
) {
let args = [];
codegen_panic_inner(fx, rustc_hir::LangItem::PanicCannotUnwind, &args, source_info.span);
codegen_panic_inner(fx, reason.lang_item(), &args, source_info.span);
}
fn codegen_panic_inner<'tcx>(

View file

@ -551,7 +551,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }

View file

@ -55,7 +55,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
_llfn: RValue<'gcc>,
_mir: &mir::Body<'tcx>,
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>> {
// TODO(antoyo)
None
}

View file

@ -20,7 +20,7 @@ pub fn compute_mir_scopes<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
instance: Instance<'tcx>,
mir: &Body<'tcx>,
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
) {
// Find all scopes with variables defined in them.
let variables = if cx.sess().opts.debuginfo == DebugInfo::Full {
@ -51,7 +51,7 @@ fn make_mir_scope<'ll, 'tcx>(
instance: Instance<'tcx>,
mir: &Body<'tcx>,
variables: &Option<BitSet<SourceScope>>,
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
instantiated: &mut BitSet<SourceScope>,
scope: SourceScope,
) {
@ -84,7 +84,6 @@ fn make_mir_scope<'ll, 'tcx>(
}
let loc = cx.lookup_debug_loc(scope_data.span.lo());
let file_metadata = file_metadata(cx, &loc.file);
let dbg_scope = match scope_data.inlined {
Some((callee, _)) => {
@ -95,18 +94,26 @@ fn make_mir_scope<'ll, 'tcx>(
ty::ParamEnv::reveal_all(),
ty::EarlyBinder::bind(callee),
);
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
cx.dbg_scope_fn(callee, callee_fn_abi, None)
debug_context.inlined_function_scopes.entry(callee).or_insert_with(|| {
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
cx.dbg_scope_fn(callee, callee_fn_abi, None)
})
}
None => {
let file_metadata = file_metadata(cx, &loc.file);
debug_context
.lexical_blocks
.entry((parent_scope.dbg_scope, loc.line, loc.col, file_metadata))
.or_insert_with(|| unsafe {
llvm::LLVMRustDIBuilderCreateLexicalBlock(
DIB(cx),
parent_scope.dbg_scope,
file_metadata,
loc.line,
loc.col,
)
})
}
None => unsafe {
llvm::LLVMRustDIBuilderCreateLexicalBlock(
DIB(cx),
parent_scope.dbg_scope,
file_metadata,
loc.line,
loc.col,
)
},
};
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {

View file

@ -5,7 +5,7 @@ use rustc_codegen_ssa::mir::debuginfo::VariableKind::*;
use self::metadata::{file_metadata, type_di_node};
use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER};
use self::namespace::mangled_name_of_instance;
use self::utils::{create_DIArray, is_node_local_to_unit, DIB};
use self::utils::{create_DIArray, debug_context, is_node_local_to_unit, DIB};
use crate::abi::FnAbi;
use crate::builder::Builder;
@ -67,6 +67,8 @@ pub struct CodegenUnitDebugContext<'ll, 'tcx> {
type_map: metadata::TypeMap<'ll, 'tcx>,
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
recursion_marker_type: OnceCell<&'ll DIType>,
/// Maps a variable (name, scope, kind (argument or local), span) to its debug information.
variables: RefCell<FxHashMap<(Symbol, &'ll DIScope, VariableKind, Span), &'ll DIVariable>>,
}
impl Drop for CodegenUnitDebugContext<'_, '_> {
@ -91,6 +93,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
type_map: Default::default(),
namespace_map: RefCell::new(Default::default()),
recursion_marker_type: OnceCell::new(),
variables: RefCell::new(Default::default()),
}
}
@ -292,7 +295,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
llfn: &'ll Value,
mir: &mir::Body<'tcx>,
) -> Option<FunctionDebugContext<&'ll DIScope, &'ll DILocation>> {
) -> Option<FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>> {
if self.sess().opts.debuginfo == DebugInfo::None {
return None;
}
@ -304,8 +307,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
file_start_pos: BytePos(0),
file_end_pos: BytePos(0),
};
let mut fn_debug_context =
FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) };
let mut fn_debug_context = FunctionDebugContext {
scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes),
inlined_function_scopes: Default::default(),
lexical_blocks: Default::default(),
};
// Fill in all the scopes, with the information from the MIR body.
compute_mir_scopes(self, instance, mir, &mut fn_debug_context);
@ -606,33 +612,39 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
variable_kind: VariableKind,
span: Span,
) -> &'ll DIVariable {
let loc = self.lookup_debug_loc(span.lo());
let file_metadata = file_metadata(self, &loc.file);
debug_context(self)
.variables
.borrow_mut()
.entry((variable_name, scope_metadata, variable_kind, span))
.or_insert_with(|| {
let loc = self.lookup_debug_loc(span.lo());
let file_metadata = file_metadata(self, &loc.file);
let type_metadata = type_di_node(self, variable_type);
let type_metadata = type_di_node(self, variable_type);
let (argument_index, dwarf_tag) = match variable_kind {
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
LocalVariable => (0, DW_TAG_auto_variable),
};
let align = self.align_of(variable_type);
let (argument_index, dwarf_tag) = match variable_kind {
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
LocalVariable => (0, DW_TAG_auto_variable),
};
let align = self.align_of(variable_type);
let name = variable_name.as_str();
unsafe {
llvm::LLVMRustDIBuilderCreateVariable(
DIB(self),
dwarf_tag,
scope_metadata,
name.as_ptr().cast(),
name.len(),
file_metadata,
loc.line,
type_metadata,
true,
DIFlags::FlagZero,
argument_index,
align.bytes() as u32,
)
}
let name = variable_name.as_str();
unsafe {
llvm::LLVMRustDIBuilderCreateVariable(
DIB(self),
dwarf_tag,
scope_metadata,
name.as_ptr().cast(),
name.len(),
file_metadata,
loc.line,
type_metadata,
true,
DIFlags::FlagZero,
argument_index,
align.bytes() as u32,
)
}
})
}
}

View file

@ -285,7 +285,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
match data.terminator().kind {
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable

View file

@ -12,7 +12,7 @@ use crate::MemFlags;
use rustc_ast as ast;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::{self, AssertKind, SwitchTargets};
use rustc_middle::mir::{self, AssertKind, SwitchTargets, UnwindTerminateReason};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Instance, Ty};
@ -178,7 +178,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
mir::UnwindAction::Continue => None,
mir::UnwindAction::Unreachable => None,
mir::UnwindAction::Terminate => {
mir::UnwindAction::Terminate(reason) => {
if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) {
// MSVC SEH will abort automatically if an exception tries to
// propagate out from cleanup.
@ -191,7 +191,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
None
} else {
Some(fx.terminate_block())
Some(fx.terminate_block(reason))
}
}
};
@ -264,7 +264,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
) -> MergingSucc {
let unwind_target = match unwind {
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
mir::UnwindAction::Terminate => Some(fx.terminate_block()),
mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)),
mir::UnwindAction::Continue => None,
mir::UnwindAction::Unreachable => None,
};
@ -649,12 +649,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
helper: TerminatorCodegenHelper<'tcx>,
bx: &mut Bx,
terminator: &mir::Terminator<'tcx>,
reason: UnwindTerminateReason,
) {
let span = terminator.source_info.span;
self.set_debug_loc(bx, terminator.source_info);
// Obtain the panic entry point.
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), LangItem::PanicCannotUnwind);
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), reason.lang_item());
// Codegen the actual panic invoke/call.
let merging_succ = helper.do_call(
@ -1229,8 +1230,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
MergingSucc::False
}
mir::TerminatorKind::UnwindTerminate => {
self.codegen_terminate_terminator(helper, bx, terminator);
mir::TerminatorKind::UnwindTerminate(reason) => {
self.codegen_terminate_terminator(helper, bx, terminator, reason);
MergingSucc::False
}
@ -1579,79 +1580,81 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
})
}
fn terminate_block(&mut self) -> Bx::BasicBlock {
self.terminate_block.unwrap_or_else(|| {
let funclet;
let llbb;
let mut bx;
if base::wants_msvc_seh(self.cx.sess()) {
// This is a basic block that we're aborting the program for,
// notably in an `extern` function. These basic blocks are inserted
// so that we assert that `extern` functions do indeed not panic,
// and if they do we abort the process.
//
// On MSVC these are tricky though (where we're doing funclets). If
// we were to do a cleanuppad (like below) the normal functions like
// `longjmp` would trigger the abort logic, terminating the
// program. Instead we insert the equivalent of `catch(...)` for C++
// which magically doesn't trigger when `longjmp` files over this
// frame.
//
// Lots more discussion can be found on #48251 but this codegen is
// modeled after clang's for:
//
// try {
// foo();
// } catch (...) {
// bar();
// }
//
// which creates an IR snippet like
//
// cs_terminate:
// %cs = catchswitch within none [%cp_terminate] unwind to caller
// cp_terminate:
// %cp = catchpad within %cs [null, i32 64, null]
// ...
fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock {
if let Some((cached_bb, cached_reason)) = self.terminate_block && reason == cached_reason {
return cached_bb;
}
llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
let funclet;
let llbb;
let mut bx;
if base::wants_msvc_seh(self.cx.sess()) {
// This is a basic block that we're aborting the program for,
// notably in an `extern` function. These basic blocks are inserted
// so that we assert that `extern` functions do indeed not panic,
// and if they do we abort the process.
//
// On MSVC these are tricky though (where we're doing funclets). If
// we were to do a cleanuppad (like below) the normal functions like
// `longjmp` would trigger the abort logic, terminating the
// program. Instead we insert the equivalent of `catch(...)` for C++
// which magically doesn't trigger when `longjmp` files over this
// frame.
//
// Lots more discussion can be found on #48251 but this codegen is
// modeled after clang's for:
//
// try {
// foo();
// } catch (...) {
// bar();
// }
//
// which creates an IR snippet like
//
// cs_terminate:
// %cs = catchswitch within none [%cp_terminate] unwind to caller
// cp_terminate:
// %cp = catchpad within %cs [null, i32 64, null]
// ...
let mut cs_bx = Bx::build(self.cx, llbb);
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);
llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
// The "null" here is actually a RTTI type descriptor for the
// C++ personality function, but `catch (...)` has no type so
// it's null. The 64 here is actually a bitfield which
// represents that this is a catch-all block.
bx = Bx::build(self.cx, cp_llbb);
let null =
bx.const_null(bx.type_ptr_ext(bx.cx().data_layout().instruction_address_space));
let sixty_four = bx.const_i32(64);
funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));
} else {
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
bx = Bx::build(self.cx, llbb);
let mut cs_bx = Bx::build(self.cx, llbb);
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);
let llpersonality = self.cx.eh_personality();
bx.filter_landing_pad(llpersonality);
// The "null" here is actually a RTTI type descriptor for the
// C++ personality function, but `catch (...)` has no type so
// it's null. The 64 here is actually a bitfield which
// represents that this is a catch-all block.
bx = Bx::build(self.cx, cp_llbb);
let null =
bx.const_null(bx.type_ptr_ext(bx.cx().data_layout().instruction_address_space));
let sixty_four = bx.const_i32(64);
funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));
} else {
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
bx = Bx::build(self.cx, llbb);
funclet = None;
}
let llpersonality = self.cx.eh_personality();
bx.filter_landing_pad(llpersonality);
self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
funclet = None;
}
let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicCannotUnwind);
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
let llret = bx.call(fn_ty, None, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
bx.do_not_inline(llret);
let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, reason.lang_item());
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
bx.unreachable();
let llret = bx.call(fn_ty, None, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
bx.do_not_inline(llret);
self.terminate_block = Some(llbb);
llbb
})
bx.unreachable();
self.terminate_block = Some((llbb, reason));
llbb
}
/// Get the backend `BasicBlock` for a MIR `BasicBlock`, either already

View file

@ -1,10 +1,12 @@
use crate::traits::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::IndexVec;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_middle::ty::Instance;
use rustc_middle::ty::Ty;
use rustc_session::config::DebugInfo;
use rustc_span::symbol::{kw, Symbol};
@ -17,11 +19,19 @@ use super::{FunctionCx, LocalRef};
use std::ops::Range;
pub struct FunctionDebugContext<S, L> {
pub struct FunctionDebugContext<'tcx, S, L> {
/// Maps from source code to the corresponding debug info scope.
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
/// Maps from a given inlined function to its debug info declaration.
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
/// Maps from a lexical block (parent scope, line, column, file) to its debug info declaration.
/// This is particularily useful if the parent scope is an inlined function.
pub lexical_blocks: FxHashMap<(S, u32, u32, S), S>,
}
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum VariableKind {
ArgumentVariable(usize /*index*/),
LocalVariable,

View file

@ -5,6 +5,7 @@ use rustc_index::IndexVec;
use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::mir::traversal;
use rustc_middle::mir::UnwindTerminateReason;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_target::abi::call::{FnAbi, PassMode};
@ -45,7 +46,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
mir: &'tcx mir::Body<'tcx>,
debug_context: Option<FunctionDebugContext<Bx::DIScope, Bx::DILocation>>,
debug_context: Option<FunctionDebugContext<'tcx, Bx::DIScope, Bx::DILocation>>,
llfn: Bx::Function,
@ -83,8 +84,8 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
/// Cached unreachable block
unreachable_block: Option<Bx::BasicBlock>,
/// Cached terminate upon unwinding block
terminate_block: Option<Bx::BasicBlock>,
/// Cached terminate upon unwinding block and its reason
terminate_block: Option<(Bx::BasicBlock, UnwindTerminateReason)>,
/// The location where each MIR arg/var/tmp/ret is stored. This is
/// usually an `PlaceRef` representing an alloca, but not always:
@ -174,7 +175,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let mut start_bx = Bx::build(cx, start_llbb);
if mir.basic_blocks.iter().any(|bb| {
bb.is_cleanup || matches!(bb.terminator().unwind(), Some(mir::UnwindAction::Terminate))
bb.is_cleanup || matches!(bb.terminator().unwind(), Some(mir::UnwindAction::Terminate(_)))
}) {
start_bx.set_personality_fn(cx.eh_personality());
}

View file

@ -26,7 +26,7 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
llfn: Self::Function,
mir: &mir::Body<'tcx>,
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>>;
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>>;
// FIXME(eddyb) find a common convention for all of the debuginfo-related
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).

View file

@ -756,6 +756,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
///
/// If `target` is `UnwindAction::Unreachable`, that indicates the function does not allow
/// unwinding, and doing so is UB.
#[cold] // usually we have normal returns, not unwinding
pub fn unwind_to_block(&mut self, target: mir::UnwindAction) -> InterpResult<'tcx> {
self.frame_mut().loc = match target {
mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
@ -763,9 +764,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
mir::UnwindAction::Unreachable => {
throw_ub_custom!(fluent::const_eval_unreachable_unwind);
}
mir::UnwindAction::Terminate => {
mir::UnwindAction::Terminate(reason) => {
self.frame_mut().loc = Right(self.frame_mut().body.span);
M::unwind_terminate(self)?;
M::unwind_terminate(self, reason)?;
// This might have pushed a new stack frame, or it terminated execution.
// Either way, `loc` will not be updated.
return Ok(());

View file

@ -18,7 +18,7 @@ use crate::const_eval::CheckAlignment;
use super::{
AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, InterpCx,
InterpResult, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
InterpResult, MPlaceTy, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
};
/// Data returned by Machine::stack_pop,
@ -222,7 +222,10 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx>;
/// Called when unwinding reached a state where execution should be terminated.
fn unwind_terminate(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>;
fn unwind_terminate(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
reason: mir::UnwindTerminateReason,
) -> InterpResult<'tcx>;
/// Called for all binary operations where the LHS has pointer type.
///
@ -462,6 +465,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
/// Called immediately after a stack frame got popped, but before jumping back to the caller.
/// The `locals` have already been destroyed!
#[inline(always)]
fn after_stack_pop(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_frame: Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
@ -471,6 +475,18 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
assert!(!unwinding);
Ok(StackPopJump::Normal)
}
/// Called immediately after actual memory was allocated for a local
/// but before the local's stack frame is updated to point to that memory.
#[inline(always)]
fn after_local_allocated(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_frame: usize,
_local: mir::Local,
_mplace: &MPlaceTy<'tcx, Self::Provenance>,
) -> InterpResult<'tcx> {
Ok(())
}
}
/// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
@ -501,7 +517,10 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
}
#[inline(always)]
fn unwind_terminate(_ecx: &mut InterpCx<$mir, $tcx, Self>) -> InterpResult<$tcx> {
fn unwind_terminate(
_ecx: &mut InterpCx<$mir, $tcx, Self>,
_reason: mir::UnwindTerminateReason,
) -> InterpResult<$tcx> {
unreachable!("unwinding cannot happen during compile-time evaluation")
}

View file

@ -899,7 +899,7 @@ where
if local_layout.is_unsized() {
throw_unsup_format!("unsized locals are not supported");
}
let mplace = *self.allocate(local_layout, MemoryKind::Stack)?;
let mplace = self.allocate(local_layout, MemoryKind::Stack)?;
// Preserve old value. (As an optimization, we can skip this if it was uninit.)
if !matches!(local_val, Immediate::Uninit) {
// We don't have to validate as we can assume the local was already
@ -909,15 +909,16 @@ where
local_val,
local_layout,
local_layout.align.abi,
mplace,
*mplace,
)?;
}
M::after_local_allocated(self, frame, local, &mplace)?;
// Now we can call `access_mut` again, asserting it goes well, and actually
// overwrite things. This points to the entire allocation, not just the part
// the place refers to, i.e. we do this before we apply `offset`.
*M::access_local_mut(self, frame, local).unwrap() =
Operand::Indirect(mplace);
mplace
Operand::Indirect(*mplace);
*mplace
}
&mut Operand::Indirect(mplace) => mplace, // this already was an indirect local
};

View file

@ -196,8 +196,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
UnwindTerminate => {
M::unwind_terminate(self)?;
UnwindTerminate(reason) => {
M::unwind_terminate(self, reason)?;
}
// When we encounter Resume, we've finished unwinding

View file

@ -1037,7 +1037,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.check_op(ops::Generator(hir::GeneratorKind::Gen))
}
TerminatorKind::UnwindTerminate => {
TerminatorKind::UnwindTerminate(_) => {
// Cleanup blocks are skipped for const checking (see `visit_basic_block_data`).
span_bug!(self.span, "`Terminate` terminator outside of cleanup block")
}

View file

@ -106,7 +106,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
}
}
mir::TerminatorKind::UnwindTerminate
mir::TerminatorKind::UnwindTerminate(_)
| mir::TerminatorKind::Call { .. }
| mir::TerminatorKind::Assert { .. }
| mir::TerminatorKind::FalseEdge { .. }

View file

@ -10,8 +10,8 @@ use rustc_middle::mir::{
traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
MirPass, MirPhase, NonDivergingIntrinsic, NullOp, Operand, Place, PlaceElem, PlaceRef,
ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
Terminator, TerminatorKind, UnOp, UnwindAction, VarDebugInfo, VarDebugInfoContents,
START_BLOCK,
Terminator, TerminatorKind, UnOp, UnwindAction, UnwindTerminateReason, VarDebugInfo,
VarDebugInfoContents, START_BLOCK,
};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
use rustc_mir_dataflow::impls::MaybeStorageLive;
@ -274,7 +274,16 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
self.fail(location, "`UnwindAction::Continue` in no-unwind function");
}
}
UnwindAction::Unreachable | UnwindAction::Terminate => (),
UnwindAction::Terminate(UnwindTerminateReason::InCleanup) => {
if !is_cleanup {
self.fail(
location,
"`UnwindAction::Terminate(InCleanup)` in a non-cleanup block",
);
}
}
// These are allowed everywhere.
UnwindAction::Unreachable | UnwindAction::Terminate(UnwindTerminateReason::Abi) => (),
}
}
}
@ -501,7 +510,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
self.fail(location, "Cannot `UnwindResume` in a function that cannot unwind")
}
}
TerminatorKind::UnwindTerminate => {
TerminatorKind::UnwindTerminate(_) => {
let bb = location.block;
if !self.body.basic_blocks[bb].is_cleanup {
self.fail(location, "Cannot `UnwindTerminate` from non-cleanup basic block")
@ -1233,7 +1242,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable => {}
}

View file

@ -40,7 +40,7 @@ where
}
}
fn is_within_packed<'tcx, L>(
pub fn is_within_packed<'tcx, L>(
tcx: TyCtxt<'tcx>,
local_decls: &L,
place: Place<'tcx>,

View file

@ -5,7 +5,7 @@ mod check_validity_requirement;
mod compare_types;
mod type_name;
pub use self::alignment::is_disaligned;
pub use self::alignment::{is_disaligned, is_within_packed};
pub use self::check_validity_requirement::check_validity_requirement;
pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
pub use self::type_name::type_name;

View file

@ -37,7 +37,7 @@
#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#![deny(unsafe_op_in_unsafe_fn)]
#[macro_use]

View file

@ -11,9 +11,14 @@ pub struct Mmap(Vec<u8>);
#[cfg(not(target_arch = "wasm32"))]
impl Mmap {
/// # Safety
///
/// The given file must not be mutated (i.e., not written, not truncated, ...) until the mapping is closed.
///
/// However in practice most callers do not ensure this, so uses of this function are likely unsound.
#[inline]
pub unsafe fn map(file: File) -> io::Result<Self> {
// Safety: this is in fact not safe.
// Safety: the caller must ensure that this is safe.
unsafe { memmap2::Mmap::map(&file).map(Mmap) }
}
}

View file

@ -1,31 +1,26 @@
use crate::fx::{FxHashMap, FxHasher};
#[cfg(parallel_compiler)]
use crate::sync::is_dyn_thread_safe;
use crate::sync::{CacheAligned, Lock, LockGuard};
use crate::sync::{is_dyn_thread_safe, CacheAligned};
use crate::sync::{Lock, LockGuard};
use std::borrow::Borrow;
use std::collections::hash_map::RawEntryMut;
use std::hash::{Hash, Hasher};
use std::mem;
#[cfg(parallel_compiler)]
// 32 shards is sufficient to reduce contention on an 8-core Ryzen 7 1700,
// but this should be tested on higher core count CPUs. How the `Sharded` type gets used
// may also affect the ideal number of shards.
const SHARD_BITS: usize = 5;
#[cfg(not(parallel_compiler))]
const SHARD_BITS: usize = 0;
pub const SHARDS: usize = 1 << SHARD_BITS;
#[cfg(parallel_compiler)]
const SHARDS: usize = 1 << SHARD_BITS;
/// An array of cache-line aligned inner locked structures with convenience methods.
pub struct Sharded<T> {
/// This mask is used to ensure that accesses are inbounds of `shards`.
/// When dynamic thread safety is off, this field is set to 0 causing only
/// a single shard to be used for greater cache efficiency.
/// A single field is used when the compiler uses only one thread.
pub enum Sharded<T> {
Single(Lock<T>),
#[cfg(parallel_compiler)]
mask: usize,
shards: [CacheAligned<Lock<T>>; SHARDS],
Shards(Box<[CacheAligned<Lock<T>>; SHARDS]>),
}
impl<T: Default> Default for Sharded<T> {
@ -38,35 +33,24 @@ impl<T: Default> Default for Sharded<T> {
impl<T> Sharded<T> {
#[inline]
pub fn new(mut value: impl FnMut() -> T) -> Self {
Sharded {
#[cfg(parallel_compiler)]
mask: if is_dyn_thread_safe() { SHARDS - 1 } else { 0 },
shards: [(); SHARDS].map(|()| CacheAligned(Lock::new(value()))),
}
}
#[inline(always)]
fn mask(&self) -> usize {
#[cfg(parallel_compiler)]
{
if SHARDS == 1 { 0 } else { self.mask }
if is_dyn_thread_safe() {
return Sharded::Shards(Box::new(
[(); SHARDS].map(|()| CacheAligned(Lock::new(value()))),
));
}
#[cfg(not(parallel_compiler))]
{
0
}
}
#[inline(always)]
fn count(&self) -> usize {
// `self.mask` is always one below the used shard count
self.mask() + 1
Sharded::Single(Lock::new(value()))
}
/// The shard is selected by hashing `val` with `FxHasher`.
#[inline]
pub fn get_shard_by_value<K: Hash + ?Sized>(&self, val: &K) -> &Lock<T> {
self.get_shard_by_hash(if SHARDS == 1 { 0 } else { make_hash(val) })
pub fn get_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> &Lock<T> {
match self {
Self::Single(single) => &single,
#[cfg(parallel_compiler)]
Self::Shards(..) => self.get_shard_by_hash(make_hash(_val)),
}
}
#[inline]
@ -75,20 +59,44 @@ impl<T> Sharded<T> {
}
#[inline]
pub fn get_shard_by_index(&self, i: usize) -> &Lock<T> {
// SAFETY: The index get ANDed with the mask, ensuring it is always inbounds.
unsafe { &self.shards.get_unchecked(i & self.mask()).0 }
pub fn get_shard_by_index(&self, _i: usize) -> &Lock<T> {
match self {
Self::Single(single) => &single,
#[cfg(parallel_compiler)]
Self::Shards(shards) => {
// SAFETY: The index gets ANDed with the shard mask, ensuring it is always inbounds.
unsafe { &shards.get_unchecked(_i & (SHARDS - 1)).0 }
}
}
}
pub fn lock_shards(&self) -> Vec<LockGuard<'_, T>> {
(0..self.count()).map(|i| self.get_shard_by_index(i).lock()).collect()
match self {
Self::Single(single) => vec![single.lock()],
#[cfg(parallel_compiler)]
Self::Shards(shards) => shards.iter().map(|shard| shard.0.lock()).collect(),
}
}
pub fn try_lock_shards(&self) -> Option<Vec<LockGuard<'_, T>>> {
(0..self.count()).map(|i| self.get_shard_by_index(i).try_lock()).collect()
match self {
Self::Single(single) => Some(vec![single.try_lock()?]),
#[cfg(parallel_compiler)]
Self::Shards(shards) => shards.iter().map(|shard| shard.0.try_lock()).collect(),
}
}
}
#[inline]
pub fn shards() -> usize {
#[cfg(parallel_compiler)]
if is_dyn_thread_safe() {
return SHARDS;
}
1
}
pub type ShardedHashMap<K, V> = Sharded<FxHashMap<K, V>>;
impl<K: Eq, V> ShardedHashMap<K, V> {

View file

@ -171,3 +171,9 @@ impl<T> Deref for WorkerLocal<T> {
unsafe { &self.locals.get_unchecked(self.registry.id().verify()).0 }
}
}
impl<T: Default> Default for WorkerLocal<T> {
fn default() -> Self {
WorkerLocal::new(|_| T::default())
}
}

View file

@ -140,12 +140,6 @@ pub const EXIT_FAILURE: i32 = 1;
pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"];
const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"];
pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T {
match result {
Err(..) => {
@ -1250,47 +1244,6 @@ fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
}
}
/// Gets a list of extra command-line flags provided by the user, as strings.
///
/// This function is used during ICEs to show more information useful for
/// debugging, since some ICEs only happens with non-default compiler flags
/// (and the users don't always report them).
fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
let mut args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable();
let mut result = Vec::new();
let mut excluded_cargo_defaults = false;
while let Some(arg) = args.next() {
if let Some(a) = ICE_REPORT_COMPILER_FLAGS.iter().find(|a| arg.starts_with(*a)) {
let content = if arg.len() == a.len() {
// A space-separated option, like `-C incremental=foo` or `--crate-type rlib`
match args.next() {
Some(arg) => arg.to_string(),
None => continue,
}
} else if arg.get(a.len()..a.len() + 1) == Some("=") {
// An equals option, like `--crate-type=rlib`
arg[a.len() + 1..].to_string()
} else {
// A non-space option, like `-Cincremental=foo`
arg[a.len()..].to_string()
};
let option = content.split_once('=').map(|s| s.0).unwrap_or(&content);
if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| option == *exc) {
excluded_cargo_defaults = true;
} else {
result.push(a.to_string());
match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| option == **s) {
Some(s) => result.push(format!("{s}=[REDACTED]")),
None => result.push(content),
}
}
}
}
if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
}
/// Runs a closure and catches unwinds triggered by fatal errors.
///
/// The compiler currently unwinds with a special sentinel value to abort
@ -1449,7 +1402,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
None
};
if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
if let Some((flags, excluded_cargo_defaults)) = rustc_session::utils::extra_compiler_flags() {
handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") });
if excluded_cargo_defaults {
handler.emit_note(session_diagnostics::IceExcludeCargoDefaults);

View file

@ -7,8 +7,8 @@ trait Trait {
type Bar;
}
type Foo = Trait; // error: the value of the associated type `Bar` (from
// the trait `Trait`) must be specified
type Foo = dyn Trait; // error: the value of the associated type `Bar` (from
// the trait `Trait`) must be specified
```
Trait objects need to have all associated types specified. Please verify that
@ -20,5 +20,5 @@ trait Trait {
type Bar;
}
type Foo = Trait<Bar=i32>; // ok!
type Foo = dyn Trait<Bar=i32>; // ok!
```

View file

@ -4,7 +4,7 @@
#![feature(type_alias_impl_trait)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#[macro_use]
extern crate tracing;

View file

@ -15,7 +15,7 @@
#![feature(box_patterns)]
#![feature(error_reporter)]
#![allow(incomplete_features)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#[macro_use]
extern crate rustc_macros;
@ -197,8 +197,14 @@ impl CodeSuggestion {
use rustc_span::{CharPos, Pos};
/// Append to a buffer the remainder of the line of existing source code, and return the
/// count of lines that have been added for accurate highlighting.
/// Extracts a substring from the provided `line_opt` based on the specified low and high indices,
/// appends it to the given buffer `buf`, and returns the count of newline characters in the substring
/// for accurate highlighting.
/// If `line_opt` is `None`, a newline character is appended to the buffer, and 0 is returned.
///
/// ## Returns
///
/// The count of newline characters in the extracted substring.
fn push_trailing(
buf: &mut String,
line_opt: Option<&Cow<'_, str>>,
@ -206,22 +212,30 @@ impl CodeSuggestion {
hi_opt: Option<&Loc>,
) -> usize {
let mut line_count = 0;
// Convert CharPos to Usize, as CharPose is character offset
// Extract low index and high index
let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
if let Some(line) = line_opt {
if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
// Get high index while account for rare unicode and emoji with char_indices
let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
match hi_opt {
// If high index exist, take string from low to high index
Some(hi) if hi > lo => {
// count how many '\n' exist
line_count = line[lo..hi].matches('\n').count();
buf.push_str(&line[lo..hi])
}
Some(_) => (),
// If high index absence, take string from low index till end string.len
None => {
// count how many '\n' exist
line_count = line[lo..].matches('\n').count();
buf.push_str(&line[lo..])
}
}
}
// If high index is None
if hi_opt.is_none() {
buf.push('\n');
}

View file

@ -11,7 +11,7 @@
#![feature(try_blocks)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#[macro_use]
extern crate rustc_macros;

View file

@ -282,7 +282,7 @@ declare_features! (
(active, arm_target_feature, "1.27.0", Some(44839), None),
(active, avx512_target_feature, "1.27.0", Some(44839), None),
(active, bpf_target_feature, "1.54.0", Some(44839), None),
(active, csky_target_feature, "CURRENT_RUSTC_VERSION", Some(44839), None),
(active, csky_target_feature, "1.73.0", Some(44839), None),
(active, ermsb_target_feature, "1.49.0", Some(44839), None),
(active, hexagon_target_feature, "1.27.0", Some(44839), None),
(active, mips_target_feature, "1.27.0", Some(44839), None),
@ -315,7 +315,7 @@ declare_features! (
/// Allows `extern "ptx-*" fn()`.
(active, abi_ptx, "1.15.0", Some(38788), None),
/// Allows `extern "riscv-interrupt-m" fn()` and `extern "riscv-interrupt-s" fn()`.
(active, abi_riscv_interrupt, "CURRENT_RUSTC_VERSION", Some(111889), None),
(active, abi_riscv_interrupt, "1.73.0", Some(111889), None),
/// Allows `extern "x86-interrupt" fn()`.
(active, abi_x86_interrupt, "1.17.0", Some(40180), None),
/// Allows additional const parameter types, such as `&'static str` or user defined types
@ -341,7 +341,7 @@ declare_features! (
/// Allows async functions to be declared, implemented, and used in traits.
(active, async_fn_in_trait, "1.66.0", Some(91611), None),
/// Allows `#[track_caller]` on async functions.
(active, async_fn_track_caller, "CURRENT_RUSTC_VERSION", Some(110011), None),
(active, async_fn_track_caller, "1.73.0", Some(110011), None),
/// Allows builtin # foo() syntax
(active, builtin_syntax, "1.71.0", Some(110680), None),
/// Allows `c"foo"` literals.
@ -353,7 +353,7 @@ declare_features! (
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
(active, cfg_overflow_checks, "1.71.0", Some(111466), None),
/// Provides the relocation model information as cfg entry
(active, cfg_relocation_model, "CURRENT_RUSTC_VERSION", Some(114929), None),
(active, cfg_relocation_model, "1.73.0", Some(114929), None),
/// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used.
(active, cfg_sanitize, "1.41.0", Some(39699), None),
/// Allows `cfg(target_abi = "...")`.
@ -411,7 +411,7 @@ declare_features! (
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
(active, deprecated_suggestion, "1.61.0", Some(94785), None),
/// Allows using the `#[diagnostic]` attribute tool namespace
(active, diagnostic_namespace, "CURRENT_RUSTC_VERSION", Some(94785), None),
(active, diagnostic_namespace, "1.73.0", Some(94785), None),
/// Controls errors in trait implementations.
(active, do_not_recommend, "1.67.0", Some(51992), None),
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
@ -456,7 +456,7 @@ declare_features! (
/// Allows non-trivial generic constants which have to have wfness manually propagated to callers
(incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
/// Allows generic parameters and where-clauses on free & associated const items.
(incomplete, generic_const_items, "CURRENT_RUSTC_VERSION", Some(113521), None),
(incomplete, generic_const_items, "1.73.0", Some(113521), None),
/// Allows using `..=X` as a patterns in slices.
(active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None),
/// Allows `if let` guard in match arms.
@ -584,6 +584,8 @@ declare_features! (
(active, type_privacy_lints, "1.72.0", Some(48054), None),
/// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE.
(active, unix_sigpipe, "1.65.0", Some(97889), None),
/// Allows unnamed fields of struct and union type
(incomplete, unnamed_fields, "CURRENT_RUSTC_VERSION", Some(49804), None),
/// Allows unsized fn parameters.
(active, unsized_fn_params, "1.49.0", Some(48055), None),
/// Allows unsized rvalues at arguments and parameters.

View file

@ -238,6 +238,7 @@ language_item_table! {
PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None;
PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
PanicInCleanup, sym::panic_in_cleanup, panic_in_cleanup, Target::Fn, GenericRequirement::Exact(0);
/// libstd panic entry point. Necessary for const eval to be able to catch it
BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;

View file

@ -13,7 +13,7 @@
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#[macro_use]
extern crate rustc_macros;

View file

@ -597,7 +597,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}
}
if !suggestions.is_empty() {
suggestions.sort_by_key(|&(span, _)| span);
// There are cases where one bound points to a span within another bound's span, like when
// you have code like the following (#115019), so we skip providing a suggestion in those
// cases to avoid having a malformed suggestion.
//
// pub struct Flatten<I> {
// inner: <IntoIterator<Item: IntoIterator<Item: >>::IntoIterator as Item>::core,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// | ^^^^^^^^^^^^^^^^^^^^^
// | |
// | associated types `Item`, `IntoIter` must be specified
// associated types `Item`, `IntoIter` must be specified
// }
let overlaps = suggestions.windows(2).any(|pair| pair[0].0.overlaps(pair[1].0));
if !suggestions.is_empty() && !overlaps {
err.multipart_suggestion(
format!("specify the associated type{}", pluralize!(types_count)),
suggestions,

View file

@ -342,9 +342,16 @@ fn compare_method_predicate_entailment<'tcx>(
continue;
};
for obligation in obligations {
debug!(?obligation);
match obligation.predicate.kind().skip_binder() {
// We need to register Projection oblgiations too, because we may end up with
// an implied `X::Item: 'a`, which gets desugared into `X::Item = ?0`, `?0: 'a`.
// If we only register the region outlives obligation, this leads to an unconstrained var.
// See `implied_bounds_entailment_alias_var` test.
ty::PredicateKind::Clause(
ty::ClauseKind::RegionOutlives(..) | ty::ClauseKind::TypeOutlives(..),
ty::ClauseKind::RegionOutlives(..)
| ty::ClauseKind::TypeOutlives(..)
| ty::ClauseKind::Projection(..),
) => ocx.register_obligation(obligation),
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
if wf_args_seen.insert(arg) {

View file

@ -44,20 +44,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
false
}
fn check_asm_operand_type(
&self,
idx: usize,
reg: InlineAsmRegOrRegClass,
expr: &'tcx hir::Expr<'tcx>,
template: &[InlineAsmTemplatePiece],
is_input: bool,
tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
target_features: &FxIndexSet<Symbol>,
) -> Option<InlineAsmType> {
let ty = (self.get_operand_ty)(expr);
if ty.has_non_region_infer() {
bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
}
fn get_asm_ty(&self, ty: Ty<'tcx>) -> Option<InlineAsmType> {
let asm_ty_isize = match self.tcx.sess.target.pointer_width {
16 => InlineAsmType::I16,
32 => InlineAsmType::I32,
@ -65,10 +52,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
_ => unreachable!(),
};
let asm_ty = match *ty.kind() {
// `!` is allowed for input but not for output (issue #87802)
ty::Never if is_input => return None,
_ if ty.references_error() => return None,
match *ty.kind() {
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
@ -99,7 +83,6 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
};
match ty.kind() {
ty::Never | ty::Error(_) => return None,
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::VecI8(size)),
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
Some(InlineAsmType::VecI16(size))
@ -128,6 +111,38 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
}
ty::Infer(_) => unreachable!(),
_ => None,
}
}
fn check_asm_operand_type(
&self,
idx: usize,
reg: InlineAsmRegOrRegClass,
expr: &'tcx hir::Expr<'tcx>,
template: &[InlineAsmTemplatePiece],
is_input: bool,
tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
target_features: &FxIndexSet<Symbol>,
) -> Option<InlineAsmType> {
let ty = (self.get_operand_ty)(expr);
if ty.has_non_region_infer() {
bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
}
let asm_ty = match *ty.kind() {
// `!` is allowed for input but not for output (issue #87802)
ty::Never if is_input => return None,
_ if ty.references_error() => return None,
ty::Adt(adt, args) if Some(adt.did()) == self.tcx.lang_items().maybe_uninit() => {
let fields = &adt.non_enum_variant().fields;
let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args);
let ty::Adt(ty, args) = ty.kind() else { unreachable!() };
assert!(ty.is_manually_drop());
let fields = &ty.non_enum_variant().fields;
let ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args);
self.get_asm_ty(ty)
}
_ => self.get_asm_ty(ty),
};
let Some(asm_ty) = asm_ty else {
let msg = format!("cannot use value of type `{ty}` for inline assembly");

View file

@ -289,6 +289,7 @@ fn default_body_is_unstable(
&tcx.sess.parse_sess,
feature,
rustc_feature::GateIssue::Library(issue),
false,
);
err.emit();

View file

@ -1,6 +1,6 @@
use crate::FnCtxt;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_infer::{infer::type_variable::TypeVariableOriginKind, traits::ObligationCauseCode};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
@ -133,15 +133,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
// Notably, we only point to params that are local to the
// item we're checking, since those are the ones we are able
// to look in the final `hir::PathSegment` for. Everything else
// would require a deeper search into the `qpath` than I think
// is worthwhile.
if let Some(param_to_point_at) = param_to_point_at
&& self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath)
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
.into_iter()
.flatten()
{
return true;
if self.point_at_path_if_possible(error, def_id, param, qpath) {
return true;
}
}
}
hir::ExprKind::MethodCall(segment, receiver, args, ..) => {
@ -166,12 +165,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
return true;
}
// Handle `Self` param specifically, since it's separated in
// the method call representation
if self_param_to_point_at.is_some() {
error.obligation.cause.span = receiver
.span
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
.unwrap_or(receiver.span);
return true;
}
}
hir::ExprKind::Struct(qpath, fields, ..) => {
if let Res::Def(
hir::def::DefKind::Struct | hir::def::DefKind::Variant,
variant_def_id,
) = self.typeck_results.borrow().qpath_res(qpath, hir_id)
if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) =
self.typeck_results.borrow().qpath_res(qpath, hir_id)
{
for param in
[param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
@ -193,10 +199,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
if let Some(param_to_point_at) = param_to_point_at
&& self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath)
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
.into_iter()
.flatten()
{
return true;
if self.point_at_path_if_possible(error, def_id, param, qpath) {
return true;
}
}
}
_ => {}
@ -213,17 +223,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
qpath: &hir::QPath<'tcx>,
) -> bool {
match qpath {
hir::QPath::Resolved(_, path) => {
if let Some(segment) = path.segments.last()
&& self.point_at_generic_if_possible(error, def_id, param, segment)
hir::QPath::Resolved(self_ty, path) => {
for segment in path.segments.iter().rev() {
if let Res::Def(kind, def_id) = segment.res
&& !matches!(kind, DefKind::Mod | DefKind::ForeignMod)
&& self.point_at_generic_if_possible(error, def_id, param, segment)
{
return true;
}
}
// Handle `Self` param specifically, since it's separated in
// the path representation
if let Some(self_ty) = self_ty
&& let ty::GenericArgKind::Type(ty) = param.unpack()
&& ty == self.tcx.types.self_param
{
error.obligation.cause.span = self_ty
.span
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
.unwrap_or(self_ty.span);
return true;
}
}
hir::QPath::TypeRelative(_, segment) => {
hir::QPath::TypeRelative(self_ty, segment) => {
if self.point_at_generic_if_possible(error, def_id, param, segment) {
return true;
}
// Handle `Self` param specifically, since it's separated in
// the path representation
if let ty::GenericArgKind::Type(ty) = param.unpack()
&& ty == self.tcx.types.self_param
{
error.obligation.cause.span = self_ty
.span
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
.unwrap_or(self_ty.span);
return true;
}
}
_ => {}
}
@ -618,14 +654,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let variant_def_id = match expr_struct_def_kind {
hir::def::DefKind::Struct => {
DefKind::Struct => {
if in_ty_adt.did() != expr_struct_def_id {
// FIXME: Deal with type aliases?
return Err(expr);
}
expr_struct_def_id
}
hir::def::DefKind::Variant => {
DefKind::Variant => {
// If this is a variant, its parent is the type definition.
if in_ty_adt.did() != self.tcx.parent(expr_struct_def_id) {
// FIXME: Deal with type aliases?
@ -727,14 +763,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let variant_def_id = match expr_struct_def_kind {
hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => {
DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => {
if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) {
// FIXME: Deal with type aliases?
return Err(expr);
}
self.tcx.parent(expr_ctor_def_id)
}
hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => {
DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => {
// For a typical enum like
// `enum Blah<T> { Variant(T) }`
// we get the following resolutions:

View file

@ -436,6 +436,12 @@ fn fatally_break_rust(tcx: TyCtxt<'_>) {
tcx.sess.cfg_version,
config::host_triple(),
));
if let Some((flags, excluded_cargo_defaults)) = rustc_session::utils::extra_compiler_flags() {
handler.note_without_error(format!("compiler flags: {}", flags.join(" ")));
if excluded_cargo_defaults {
handler.note_without_error("some of the compiler flags provided by cargo are hidden");
}
}
}
fn has_expected_num_generic_args(tcx: TyCtxt<'_>, trait_did: DefId, expected: usize) -> bool {

View file

@ -12,7 +12,7 @@
test
)
)]
#![cfg_attr(all(not(bootstrap), feature = "nightly"), allow(internal_features))]
#![cfg_attr(feature = "nightly", allow(internal_features))]
#[cfg(feature = "nightly")]
pub mod bit_set;
@ -29,6 +29,18 @@ pub use {idx::Idx, slice::IndexSlice, vec::IndexVec};
pub use rustc_macros::newtype_index;
/// Type size assertion. The first argument is a type and the second argument is its expected size.
///
/// <div class="warning">
///
/// Emitting hard errors from size assertions like this is generally not
/// recommended, especially in libraries, because they can cause build failures if the layout
/// algorithm or dependencies change. Here in rustc we control the toolchain and layout algorithm,
/// so the former is not a problem. For the latter we have a lockfile as rustc is an application and
/// precompiled library.
///
/// Short version: Don't copy this macro into your own code. Use a `#[test]` instead.
///
/// </div>
#[macro_export]
macro_rules! static_assert_size {
($ty:ty, $size:expr) => {

View file

@ -821,7 +821,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(profile_emit, Some(PathBuf::from("abc")));
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
tracked!(profiler_runtime, "abc".to_string());
tracked!(relax_elf_relocations, Some(true));
tracked!(relax_elf_relocations, Some(false));
tracked!(relro_level, Some(RelroLevel::Full));
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
tracked!(report_delayed_bugs, true);

View file

@ -158,13 +158,15 @@ lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}`
lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name}
lint_check_name_removed = lint `{$lint_name}` has been removed: {$reason}
lint_check_name_renamed = lint `{$lint_name}` has been renamed to `{$replace}`
lint_check_name_unknown = unknown lint: `{$lint_name}`
.help = did you mean: `{$suggestion}`
lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`
lint_check_name_warning = {$msg}
lint_command_line_source = `forbid` lint level was set on command line
lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike
@ -323,6 +325,8 @@ lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined be
lint_invalid_reference_casting_borrow_as_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
.label = casting happend here
lint_invalid_reference_casting_note_book = for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
lint_lintpass_by_hand = implementing `LintPass` by hand
.help = try using `declare_lint_pass!` or `impl_lint_pass!` instead
@ -482,8 +486,11 @@ lint_redundant_semicolons =
*[false] this semicolon
}
lint_renamed_or_removed_lint = {$msg}
lint_removed_lint = lint `{$name}` has been removed: {$reason}
lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}`
.suggestion = use the new name
.help = use the new name `{$replace}`
lint_requested_level = requested on the command line with `{$level} {$lint_name}`

View file

@ -17,8 +17,8 @@
use self::TargetLint::*;
use crate::errors::{
CheckNameDeprecated, CheckNameUnknown, CheckNameUnknownTool, CheckNameWarning, RequestedLevel,
UnsupportedGroup,
CheckNameDeprecated, CheckNameRemoved, CheckNameRenamed, CheckNameUnknown,
CheckNameUnknownTool, RequestedLevel, UnsupportedGroup,
};
use crate::levels::LintLevelsBuilder;
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
@ -124,9 +124,10 @@ pub enum CheckLintNameResult<'a> {
NoLint(Option<Symbol>),
/// The lint refers to a tool that has not been registered.
NoTool,
/// The lint is either renamed or removed. This is the warning
/// message, and an optional new name (`None` if removed).
Warning(String, Option<String>),
/// The lint has been renamed to a new name.
Renamed(String),
/// The lint has been removed due to the given reason.
Removed(String),
/// The lint is from a tool. If the Option is None, then either
/// the lint does not exist in the tool or the code was not
/// compiled with the tool and therefore the lint was never
@ -342,25 +343,32 @@ impl LintStore {
sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
return;
}
let lint_name = lint_name.to_string();
match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
CheckLintNameResult::Warning(msg, _) => {
sess.emit_warning(CheckNameWarning {
msg,
CheckLintNameResult::Renamed(replace) => {
sess.emit_warning(CheckNameRenamed {
lint_name,
replace: &replace,
sub: RequestedLevel { level, lint_name },
});
}
CheckLintNameResult::Removed(reason) => {
sess.emit_warning(CheckNameRemoved {
lint_name,
reason: &reason,
sub: RequestedLevel { level, lint_name },
});
}
CheckLintNameResult::NoLint(suggestion) => {
sess.emit_err(CheckNameUnknown {
lint_name: lint_name.clone(),
lint_name,
suggestion,
sub: RequestedLevel { level, lint_name },
});
}
CheckLintNameResult::Tool(Err((Some(_), new_name))) => {
sess.emit_warning(CheckNameDeprecated {
lint_name: lint_name.clone(),
new_name,
lint_name,
new_name: &new_name,
sub: RequestedLevel { level, lint_name },
});
}
@ -445,14 +453,8 @@ impl LintStore {
}
}
match self.by_name.get(&complete_name) {
Some(Renamed(new_name, _)) => CheckLintNameResult::Warning(
format!("lint `{complete_name}` has been renamed to `{new_name}`"),
Some(new_name.to_owned()),
),
Some(Removed(reason)) => CheckLintNameResult::Warning(
format!("lint `{complete_name}` has been removed: {reason}"),
None,
),
Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()),
Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()),
None => match self.lint_groups.get(&*complete_name) {
// If neither the lint, nor the lint group exists check if there is a `clippy::`
// variant of this lint
@ -966,6 +968,14 @@ pub trait LintContext: Sized {
Applicability::MachineApplicable
);
}
BuiltinLintDiagnostics::AssociatedConstElidedLifetime { elided, span } => {
db.span_suggestion_verbose(
if elided { span.shrink_to_hi() } else { span },
"use the `'static` lifetime",
if elided { "'static " } else { "'static" },
Applicability::MachineApplicable
);
}
}
// Rewrap `db`, and pass control to the user.
decorate(db)

View file

@ -91,9 +91,9 @@ pub struct BuiltinEllipsisInclusiveRangePatterns {
#[derive(Subdiagnostic)]
#[note(lint_requested_level)]
pub struct RequestedLevel {
pub struct RequestedLevel<'a> {
pub level: Level,
pub lint_name: String,
pub lint_name: &'a str,
}
#[derive(Diagnostic)]
@ -102,13 +102,13 @@ pub struct UnsupportedGroup {
pub lint_group: String,
}
pub struct CheckNameUnknown {
pub lint_name: String,
pub struct CheckNameUnknown<'a> {
pub lint_name: &'a str,
pub suggestion: Option<Symbol>,
pub sub: RequestedLevel,
pub sub: RequestedLevel<'a>,
}
impl IntoDiagnostic<'_> for CheckNameUnknown {
impl IntoDiagnostic<'_> for CheckNameUnknown<'_> {
fn into_diagnostic(
self,
handler: &Handler,
@ -127,25 +127,35 @@ impl IntoDiagnostic<'_> for CheckNameUnknown {
#[derive(Diagnostic)]
#[diag(lint_check_name_unknown_tool, code = "E0602")]
pub struct CheckNameUnknownTool {
pub struct CheckNameUnknownTool<'a> {
pub tool_name: Symbol,
#[subdiagnostic]
pub sub: RequestedLevel,
pub sub: RequestedLevel<'a>,
}
#[derive(Diagnostic)]
#[diag(lint_check_name_warning)]
pub struct CheckNameWarning {
pub msg: String,
#[diag(lint_check_name_renamed)]
pub struct CheckNameRenamed<'a> {
pub lint_name: &'a str,
pub replace: &'a str,
#[subdiagnostic]
pub sub: RequestedLevel,
pub sub: RequestedLevel<'a>,
}
#[derive(Diagnostic)]
#[diag(lint_check_name_removed)]
pub struct CheckNameRemoved<'a> {
pub lint_name: &'a str,
pub reason: &'a str,
#[subdiagnostic]
pub sub: RequestedLevel<'a>,
}
#[derive(Diagnostic)]
#[diag(lint_check_name_deprecated)]
pub struct CheckNameDeprecated {
pub lint_name: String,
pub new_name: String,
pub struct CheckNameDeprecated<'a> {
pub lint_name: &'a str,
pub new_name: &'a str,
#[subdiagnostic]
pub sub: RequestedLevel,
pub sub: RequestedLevel<'a>,
}

View file

@ -4,15 +4,15 @@ use crate::{
fluent_generated as fluent,
late::unerased_lint_store,
lints::{
DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAttributeLint,
RenamedOrRemovedLint, RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAttributeLint, RemovedLint,
RenamedLint, RenamedLintSuggestion, UnknownLint, UnknownLintSuggestion,
},
};
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
use rustc_feature::Features;
use rustc_feature::{Features, GateIssue};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirId;
@ -24,12 +24,14 @@ use rustc_middle::lint::{
};
use rustc_middle::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
builtin::{
self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES,
UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
},
Level, Lint, LintExpectationId, LintId,
};
use rustc_session::parse::{add_feature_diagnostics, feature_err};
use rustc_session::parse::feature_err;
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
@ -566,7 +568,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
continue;
}
if self.check_gated_lint(id, DUMMY_SP) {
if self.check_gated_lint(id, DUMMY_SP, true) {
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
self.insert(id, (level, src));
}
@ -837,7 +839,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
reason,
};
for &id in *ids {
if self.check_gated_lint(id, attr.span) {
if self.check_gated_lint(id, attr.span, false) {
self.insert_spec(id, (level, src));
}
}
@ -854,7 +856,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
reason,
};
for &id in ids {
if self.check_gated_lint(id, attr.span) {
if self.check_gated_lint(id, attr.span, false) {
self.insert_spec(id, (level, src));
}
}
@ -913,18 +915,26 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
_ if !self.warn_about_weird_lints => {}
CheckLintNameResult::Warning(msg, renamed) => {
CheckLintNameResult::Renamed(new_name) => {
let suggestion =
renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
suggestion: sp,
replace: replace.as_str(),
});
RenamedLintSuggestion { suggestion: sp, replace: new_name.as_str() };
let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
self.emit_spanned_lint(
RENAMED_AND_REMOVED_LINTS,
sp.into(),
RenamedOrRemovedLint { msg, suggestion },
RenamedLint { name: name.as_str(), suggestion },
);
}
CheckLintNameResult::Removed(reason) => {
let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
self.emit_spanned_lint(
RENAMED_AND_REMOVED_LINTS,
sp.into(),
RemovedLint { name: name.as_str(), reason: reason.as_str() },
);
}
CheckLintNameResult::NoLint(suggestion) => {
let name = if let Some(tool_ident) = tool_ident {
format!("{}::{}", tool_ident.name, name)
@ -943,7 +953,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
// If this lint was renamed, apply the new lint instead of ignoring the attribute.
// This happens outside of the match because the new lint should be applied even if
// we don't warn about the name change.
if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
if let CheckLintNameResult::Renamed(new_name) = lint_result {
// Ignore any errors or warnings that happen because the new name is inaccurate
// NOTE: `new_name` already includes the tool name, so we don't have to add it again.
if let CheckLintNameResult::Ok(ids) =
@ -955,7 +965,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
reason,
};
for &id in ids {
if self.check_gated_lint(id, attr.span) {
if self.check_gated_lint(id, attr.span, false) {
self.insert_spec(id, (level, src));
}
}
@ -1000,7 +1010,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
// FIXME only emit this once for each attribute, instead of repeating it 4 times for
// pre-expansion lints, post-expansion lints, `shallow_lint_levels_on` and `lint_expectations`.
#[track_caller]
fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
if let Some(feature) = lint_id.lint.feature_gate {
if !self.features.enabled(feature) {
let lint = builtin::UNKNOWN_LINTS;
@ -1015,7 +1025,13 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|lint| {
lint.set_arg("name", lint_id.lint.name_lower());
lint.note(fluent::lint_note);
add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
rustc_session::parse::add_feature_diagnostics_for_issue(
lint,
&self.sess.parse_sess,
feature,
GateIssue::Language,
lint_from_cli,
);
lint
},
);

View file

@ -40,7 +40,7 @@
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#[macro_use]
extern crate rustc_middle;

View file

@ -764,11 +764,13 @@ pub enum InvalidFromUtf8Diag {
#[derive(LintDiagnostic)]
pub enum InvalidReferenceCastingDiag {
#[diag(lint_invalid_reference_casting_borrow_as_mut)]
#[note(lint_invalid_reference_casting_note_book)]
BorrowAsMut {
#[label]
orig_cast: Option<Span>,
},
#[diag(lint_invalid_reference_casting_assign_to_ref)]
#[note(lint_invalid_reference_casting_note_book)]
AssignToRef {
#[label]
orig_cast: Option<Span>,
@ -1010,23 +1012,29 @@ pub struct DeprecatedLintName<'a> {
pub replace: &'a str,
}
// FIXME: Non-translatable msg
#[derive(LintDiagnostic)]
#[diag(lint_renamed_or_removed_lint)]
pub struct RenamedOrRemovedLint<'a> {
pub msg: &'a str,
#[diag(lint_renamed_lint)]
pub struct RenamedLint<'a> {
pub name: &'a str,
#[subdiagnostic]
pub suggestion: Option<RenamedOrRemovedLintSuggestion<'a>>,
pub suggestion: RenamedLintSuggestion<'a>,
}
#[derive(Subdiagnostic)]
#[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")]
pub struct RenamedOrRemovedLintSuggestion<'a> {
pub struct RenamedLintSuggestion<'a> {
#[primary_span]
pub suggestion: Span,
pub replace: &'a str,
}
#[derive(LintDiagnostic)]
#[diag(lint_removed_lint)]
pub struct RemovedLint<'a> {
pub name: &'a str,
pub reason: &'a str,
}
#[derive(LintDiagnostic)]
#[diag(lint_unknown_lint)]
pub struct UnknownLint {

View file

@ -56,20 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
// &mut <expr>
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
expr
// <expr> = ...
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
expr
// <expr> += ...
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
expr
} else {
return;
};
let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else {
let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
return;
};
@ -86,15 +73,58 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
cx.emit_spanned_lint(
INVALID_REFERENCE_CASTING,
expr.span,
if matches!(expr.kind, ExprKind::AddrOf(..)) {
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
} else {
if is_assignment {
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
} else {
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
},
);
}
}
fn is_operation_we_care_about<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'tcx>,
) -> Option<(bool, &'tcx Expr<'tcx>)> {
fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> {
// &mut <expr>
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
expr
// <expr> = ...
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
expr
// <expr> += ...
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
expr
} else {
return None;
};
if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind {
Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e))
} else {
None
}
}
fn ptr_write<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'tcx>,
) -> Option<(bool, &'tcx Expr<'tcx>)> {
if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind
&& let ExprKind::Path(ref qpath) = path.kind
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned))
{
Some((true, arg_ptr))
} else {
None
}
}
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
}
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
let e = e.peel_blocks();

View file

@ -3376,6 +3376,7 @@ declare_lint_pass! {
DEPRECATED_IN_FUTURE,
DEPRECATED_WHERE_CLAUSE_LOCATION,
DUPLICATE_MACRO_ATTRIBUTES,
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
ELIDED_LIFETIMES_IN_PATHS,
EXPORTED_PRIVATE_DEPENDENCIES,
FFI_UNWIND_CALLS,
@ -4527,3 +4528,44 @@ declare_lint! {
reference: "issue #114095 <https://github.com/rust-lang/rust/issues/114095>",
};
}
declare_lint! {
/// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes
/// that were erroneously allowed in associated constants.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(elided_lifetimes_in_associated_constant)]
///
/// struct Foo;
///
/// impl Foo {
/// const STR: &str = "hello, world";
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Previous version of Rust
///
/// Implicit static-in-const behavior was decided [against] for associated
/// constants because of ambiguity. This, however, regressed and the compiler
/// erroneously treats elided lifetimes in associated constants as lifetime
/// parameters on the impl.
///
/// This is a [future-incompatible] lint to transition this to a
/// hard error in the future.
///
/// [against]: https://github.com/rust-lang/rust/issues/38831
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
Warn,
"elided lifetimes cannot be used in associated constants in impls",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
reference: "issue #115010 <https://github.com/rust-lang/rust/issues/115010>",
};
}

View file

@ -23,8 +23,9 @@ pub mod builtin;
#[macro_export]
macro_rules! pluralize {
// Pluralize based on count (e.g., apples)
($x:expr) => {
if $x != 1 { "s" } else { "" }
if $x == 1 { "" } else { "s" }
};
("has", $x:expr) => {
if $x == 1 { "has" } else { "have" }
@ -572,6 +573,10 @@ pub enum BuiltinLintDiagnostics {
/// The span of the unnecessarily-qualified path to remove.
removal_span: Span,
},
AssociatedConstElidedLifetime {
elided: bool,
span: Span,
},
}
/// Lints that are buffered up early on in the `Session` before the

View file

@ -203,7 +203,12 @@ LLVMRustWriteArchive(char *Dst, size_t NumMembers,
}
}
#if LLVM_VERSION_LT(18, 0)
auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false);
#else
auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab : SymtabWritingMode::NoSymtab;
auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false);
#endif
if (!Result)
return LLVMRustResult::Success;
LLVMRustSetLastError(toString(std::move(Result)).c_str());

View file

@ -7,7 +7,7 @@
#![allow(rustc::default_hash_types)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#![recursion_limit = "128"]
use synstructure::decl_derive;

View file

@ -63,7 +63,7 @@
#![feature(macro_metavar_expr)]
#![recursion_limit = "512"]
#![allow(rustc::potential_query_instability)]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![allow(internal_features)]
#[macro_use]
extern crate bitflags;

View file

@ -1275,9 +1275,11 @@ impl<O> AssertKind<O> {
matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
}
/// Getting a description does not require `O` to be printable, and does not
/// require allocation.
/// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` separately.
/// Get the message that is printed at runtime when this assertion fails.
///
/// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
/// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
/// instead of printing a static message.
pub fn description(&self) -> &'static str {
use AssertKind::*;
match self {
@ -1303,6 +1305,11 @@ impl<O> AssertKind<O> {
}
/// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
///
/// Needs to be kept in sync with the run-time behavior (which is defined by
/// `AssertKind::description` and the lang items mentioned in its docs).
/// Note that we deliberately show more details here than we do at runtime, such as the actual
/// numbers that overflowed -- it is much easier to do so here than at runtime.
pub fn fmt_assert_args<W: Write>(&self, f: &mut W) -> fmt::Result
where
O: Debug,
@ -1358,6 +1365,12 @@ impl<O> AssertKind<O> {
}
}
/// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
///
/// Needs to be kept in sync with the run-time behavior (which is defined by
/// `AssertKind::description` and the lang items mentioned in its docs).
/// Note that we deliberately show more details here than we do at runtime, such as the actual
/// numbers that overflowed -- it is much easier to do so here than at runtime.
pub fn diagnostic_message(&self) -> DiagnosticMessage {
use crate::fluent_generated::*;
use AssertKind::*;

View file

@ -14,7 +14,8 @@ pub struct MirPatch<'tcx> {
resume_block: Option<BasicBlock>,
// Only for unreachable in cleanup path.
unreachable_cleanup_block: Option<BasicBlock>,
terminate_block: Option<BasicBlock>,
// Cached block for UnwindTerminate (with reason)
terminate_block: Option<(BasicBlock, UnwindTerminateReason)>,
body_span: Span,
next_local: usize,
}
@ -35,13 +36,15 @@ impl<'tcx> MirPatch<'tcx> {
for (bb, block) in body.basic_blocks.iter_enumerated() {
// Check if we already have a resume block
if let TerminatorKind::UnwindResume = block.terminator().kind && block.statements.is_empty() {
if matches!(block.terminator().kind, TerminatorKind::UnwindResume)
&& block.statements.is_empty()
{
result.resume_block = Some(bb);
continue;
}
// Check if we already have an unreachable block
if let TerminatorKind::Unreachable = block.terminator().kind
if matches!(block.terminator().kind, TerminatorKind::Unreachable)
&& block.statements.is_empty()
&& block.is_cleanup
{
@ -50,8 +53,10 @@ impl<'tcx> MirPatch<'tcx> {
}
// Check if we already have a terminate block
if let TerminatorKind::UnwindTerminate = block.terminator().kind && block.statements.is_empty() {
result.terminate_block = Some(bb);
if let TerminatorKind::UnwindTerminate(reason) = block.terminator().kind
&& block.statements.is_empty()
{
result.terminate_block = Some((bb, reason));
continue;
}
}
@ -93,20 +98,20 @@ impl<'tcx> MirPatch<'tcx> {
bb
}
pub fn terminate_block(&mut self) -> BasicBlock {
if let Some(bb) = self.terminate_block {
return bb;
pub fn terminate_block(&mut self, reason: UnwindTerminateReason) -> BasicBlock {
if let Some((cached_bb, cached_reason)) = self.terminate_block && reason == cached_reason {
return cached_bb;
}
let bb = self.new_block(BasicBlockData {
statements: vec![],
terminator: Some(Terminator {
source_info: SourceInfo::outermost(self.body_span),
kind: TerminatorKind::UnwindTerminate,
kind: TerminatorKind::UnwindTerminate(reason),
}),
is_cleanup: true,
});
self.terminate_block = Some(bb);
self.terminate_block = Some((bb, reason));
bb
}

View file

@ -621,7 +621,7 @@ pub enum TerminatorKind<'tcx> {
///
/// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
/// cleanup blocks.
UnwindTerminate,
UnwindTerminate(UnwindTerminateReason),
/// Returns from the function.
///
@ -813,7 +813,7 @@ impl TerminatorKind<'_> {
TerminatorKind::Goto { .. } => "Goto",
TerminatorKind::SwitchInt { .. } => "SwitchInt",
TerminatorKind::UnwindResume => "UnwindResume",
TerminatorKind::UnwindTerminate => "UnwindTerminate",
TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
TerminatorKind::Return => "Return",
TerminatorKind::Unreachable => "Unreachable",
TerminatorKind::Drop { .. } => "Drop",
@ -842,11 +842,22 @@ pub enum UnwindAction {
/// Terminates the execution if unwind happens.
///
/// Depending on the platform and situation this may cause a non-unwindable panic or abort.
Terminate,
Terminate(UnwindTerminateReason),
/// Cleanups to be done.
Cleanup(BasicBlock),
}
/// The reason we are terminating the process during unwinding.
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
#[derive(TypeFoldable, TypeVisitable)]
pub enum UnwindTerminateReason {
/// Unwinding is just not possible given the ABI of this function.
Abi,
/// We were already cleaning up for an ongoing unwind, and a *second*, *nested* unwind was
/// triggered by the drop glue.
InCleanup,
}
/// Information about an assertion failure.
#[derive(Clone, Hash, HashStable, PartialEq, Debug)]
#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]

View file

@ -1,3 +1,4 @@
use rustc_hir::LangItem;
use smallvec::SmallVec;
use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind, UnwindAction};
@ -100,6 +101,40 @@ impl<'a> Iterator for SwitchTargetsIter<'a> {
impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
impl UnwindAction {
fn cleanup_block(self) -> Option<BasicBlock> {
match self {
UnwindAction::Cleanup(bb) => Some(bb),
UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate(_) => None,
}
}
}
impl UnwindTerminateReason {
pub fn as_str(self) -> &'static str {
// Keep this in sync with the messages in `core/src/panicking.rs`.
match self {
UnwindTerminateReason::Abi => "panic in a function that cannot unwind",
UnwindTerminateReason::InCleanup => "panic in a destructor during cleanup",
}
}
/// A short representation of this used for MIR printing.
pub fn as_short_str(self) -> &'static str {
match self {
UnwindTerminateReason::Abi => "abi",
UnwindTerminateReason::InCleanup => "cleanup",
}
}
pub fn lang_item(self) -> LangItem {
match self {
UnwindTerminateReason::Abi => LangItem::PanicCannotUnwind,
UnwindTerminateReason::InCleanup => LangItem::PanicInCleanup,
}
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct Terminator<'tcx> {
pub source_info: SourceInfo,
@ -156,7 +191,7 @@ impl<'tcx> TerminatorKind<'tcx> {
Some(t).into_iter().chain((&[]).into_iter().copied())
}
UnwindResume
| UnwindTerminate
| UnwindTerminate(_)
| GeneratorDrop
| Return
| Unreachable
@ -198,7 +233,7 @@ impl<'tcx> TerminatorKind<'tcx> {
Some(t).into_iter().chain(&mut [])
}
UnwindResume
| UnwindTerminate
| UnwindTerminate(_)
| GeneratorDrop
| Return
| Unreachable
@ -215,7 +250,7 @@ impl<'tcx> TerminatorKind<'tcx> {
match *self {
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
@ -234,7 +269,7 @@ impl<'tcx> TerminatorKind<'tcx> {
match *self {
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
@ -271,18 +306,28 @@ impl<'tcx> Debug for TerminatorKind<'tcx> {
let labels = self.fmt_successor_labels();
assert_eq!(successor_count, labels.len());
let unwind = match self.unwind() {
// Not needed or included in successors
None | Some(UnwindAction::Cleanup(_)) => None,
Some(UnwindAction::Continue) => Some("unwind continue"),
Some(UnwindAction::Unreachable) => Some("unwind unreachable"),
Some(UnwindAction::Terminate) => Some("unwind terminate"),
// `Cleanup` is already included in successors
let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
write!(fmt, "unwind ")?;
match self.unwind() {
// Not needed or included in successors
None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
Some(UnwindAction::Continue) => write!(fmt, "continue"),
Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
Some(UnwindAction::Terminate(reason)) => {
write!(fmt, "terminate({})", reason.as_short_str())
}
}
};
match (successor_count, unwind) {
(0, None) => Ok(()),
(0, Some(unwind)) => write!(fmt, " -> {unwind}"),
(1, None) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
match (successor_count, show_unwind) {
(0, false) => Ok(()),
(0, true) => {
write!(fmt, " -> ")?;
fmt_unwind(fmt)
}
(1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
_ => {
write!(fmt, " -> [")?;
for (i, target) in self.successors().enumerate() {
@ -291,8 +336,9 @@ impl<'tcx> Debug for TerminatorKind<'tcx> {
}
write!(fmt, "{}: {:?}", labels[i], target)?;
}
if let Some(unwind) = unwind {
write!(fmt, ", {unwind}")?;
if show_unwind {
write!(fmt, ", ")?;
fmt_unwind(fmt)?;
}
write!(fmt, "]")
}
@ -312,7 +358,9 @@ impl<'tcx> TerminatorKind<'tcx> {
Return => write!(fmt, "return"),
GeneratorDrop => write!(fmt, "generator_drop"),
UnwindResume => write!(fmt, "resume"),
UnwindTerminate => write!(fmt, "abort"),
UnwindTerminate(reason) => {
write!(fmt, "abort({})", reason.as_short_str())
}
Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
Unreachable => write!(fmt, "unreachable"),
Drop { place, .. } => write!(fmt, "drop({place:?})"),
@ -391,7 +439,7 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
use self::TerminatorKind::*;
match *self {
Return | UnwindResume | UnwindTerminate | Unreachable | GeneratorDrop => vec![],
Return | UnwindResume | UnwindTerminate(_) | Unreachable | GeneratorDrop => vec![],
Goto { .. } => vec!["".into()],
SwitchInt { ref targets, .. } => targets
.values
@ -443,7 +491,8 @@ pub enum TerminatorEdges<'mir, 'tcx> {
/// Special action for `Yield`, `Call` and `InlineAsm` terminators.
AssignOnReturn {
return_: Option<BasicBlock>,
unwind: UnwindAction,
/// The cleanup block, if it exists.
cleanup: Option<BasicBlock>,
place: CallReturnPlaces<'mir, 'tcx>,
},
/// Special edge for `SwitchInt`.
@ -486,7 +535,7 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
use TerminatorKind::*;
match *self {
Return | UnwindResume | UnwindTerminate | GeneratorDrop | Unreachable => {
Return | UnwindResume | UnwindTerminate(_) | GeneratorDrop | Unreachable => {
TerminatorEdges::None
}
@ -496,7 +545,7 @@ impl<'tcx> TerminatorKind<'tcx> {
| Drop { target, unwind, place: _, replace: _ }
| FalseUnwind { real_target: target, unwind } => match unwind {
UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
UnwindAction::Continue | UnwindAction::Terminate | UnwindAction::Unreachable => {
UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
TerminatorEdges::Single(target)
}
},
@ -508,7 +557,7 @@ impl<'tcx> TerminatorKind<'tcx> {
Yield { resume: target, drop, resume_arg, value: _ } => {
TerminatorEdges::AssignOnReturn {
return_: Some(target),
unwind: drop.map_or(UnwindAction::Terminate, UnwindAction::Cleanup),
cleanup: drop,
place: CallReturnPlaces::Yield(resume_arg),
}
}
@ -516,7 +565,7 @@ impl<'tcx> TerminatorKind<'tcx> {
Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => {
TerminatorEdges::AssignOnReturn {
return_: target,
unwind,
cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::Call(destination),
}
}
@ -530,7 +579,7 @@ impl<'tcx> TerminatorKind<'tcx> {
unwind,
} => TerminatorEdges::AssignOnReturn {
return_: destination,
unwind,
cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::InlineAsm(operands),
},

View file

@ -470,7 +470,7 @@ macro_rules! make_mir_visitor {
match kind {
TerminatorKind::Goto { .. } |
TerminatorKind::UnwindResume |
TerminatorKind::UnwindTerminate |
TerminatorKind::UnwindTerminate(_) |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
TerminatorKind::FalseEdge { .. } |

View file

@ -2150,6 +2150,7 @@ impl<'tcx> TyCtxt<'tcx> {
for attr in self.get_attrs(did, sym::repr) {
for r in attr::parse_repr_attr(&self.sess, attr) {
flags.insert(match r {
attr::ReprRust => ReprFlags::empty(),
attr::ReprC => ReprFlags::IS_C,
attr::ReprPacked(pack) => {
let pack = Align::from_bytes(pack as u64).unwrap();

View file

@ -56,7 +56,8 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
/// Construct the MIR for a given `DefId`.
fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
// Ensure unsafeck and abstract const building is ran before we steal the THIR.
tcx.ensure_with_value().thir_check_unsafety(def);
tcx.ensure_with_value()
.thir_check_unsafety(tcx.typeck_root_def_id(def.to_def_id()).expect_local());
tcx.ensure_with_value().thir_abstract_const(def);
if let Err(e) = tcx.check_match(def) {
return construct_error(tcx, def, e);

View file

@ -370,7 +370,7 @@ impl DropTree {
let terminator = TerminatorKind::Drop {
target: blocks[drop_data.1].unwrap(),
// The caller will handle this if needed.
unwind: UnwindAction::Terminate,
unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
place: drop_data.0.local.into(),
replace: false,
};
@ -1507,7 +1507,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Yield { .. }

View file

@ -186,7 +186,7 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx
match self.body[bb].terminator().kind {
// These terminators return control flow to the caller.
TerminatorKind::UnwindTerminate
TerminatorKind::UnwindTerminate(_)
| TerminatorKind::GeneratorDrop
| TerminatorKind::UnwindResume
| TerminatorKind::Return

View file

@ -7,6 +7,7 @@ use crate::errors::*;
use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
@ -660,6 +661,17 @@ fn report_arm_reachability<'p, 'tcx>(
}
}
fn collect_non_exhaustive_tys<'p, 'tcx>(
pat: &DeconstructedPat<'p, 'tcx>,
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
) {
if matches!(pat.ctor(), Constructor::NonExhaustive) {
non_exhaustive_tys.insert(pat.ty());
}
pat.iter_fields()
.for_each(|field_pat| collect_non_exhaustive_tys(field_pat, non_exhaustive_tys))
}
/// Report that a match is not exhaustive.
fn non_exhaustive_match<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
@ -717,22 +729,27 @@ fn non_exhaustive_match<'p, 'tcx>(
scrut_ty,
if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
));
if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
&& !is_empty_match
&& witnesses.len() == 1
&& matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
{
err.note(format!(
"`{scrut_ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
exhaustively",
));
if cx.tcx.sess.is_nightly_build() {
err.help(format!(
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
enable precise `{scrut_ty}` matching",
));
if !is_empty_match && witnesses.len() == 1 {
let mut non_exhaustive_tys = FxHashSet::default();
collect_non_exhaustive_tys(&witnesses[0], &mut non_exhaustive_tys);
for ty in non_exhaustive_tys {
if ty == cx.tcx.types.usize || ty == cx.tcx.types.isize {
err.note(format!(
"`{ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
exhaustively",
));
if cx.tcx.sess.is_nightly_build() {
err.help(format!(
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
enable precise `{ty}` matching",
));
}
}
}
}
if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) {
err.note("references are always considered inhabited");

View file

@ -80,7 +80,7 @@ impl Unwind {
fn into_action(self) -> UnwindAction {
match self {
Unwind::To(bb) => UnwindAction::Cleanup(bb),
Unwind::InCleanup => UnwindAction::Terminate,
Unwind::InCleanup => UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
}
}

View file

@ -1,5 +1,5 @@
use rustc_middle::mir::{
self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges, UnwindAction,
self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges,
};
use std::ops::RangeInclusive;
@ -486,10 +486,10 @@ impl Direction for Forward {
propagate(target, exit_state);
propagate(unwind, exit_state);
}
TerminatorEdges::AssignOnReturn { return_, unwind, place } => {
TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
// This must be done *first*, otherwise the unwind path will see the assignments.
if let UnwindAction::Cleanup(unwind) = unwind {
propagate(unwind, exit_state);
if let Some(cleanup) = cleanup {
propagate(cleanup, exit_state);
}
if let Some(return_) = return_ {
analysis.apply_call_return_effect(exit_state, bb, place);

View file

@ -131,7 +131,7 @@ where
}
}
TerminatorKind::UnwindTerminate
TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Assert { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::FalseEdge { .. }

View file

@ -291,7 +291,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
// Nothing to do for these. Match exhaustively so this fails to compile when new
// variants are added.
TerminatorKind::UnwindTerminate
TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Assert { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::FalseEdge { .. }
@ -328,7 +328,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
// Nothing to do for these. Match exhaustively so this fails to compile when new
// variants are added.
TerminatorKind::Yield { .. }
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Assert { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::FalseEdge { .. }

View file

@ -371,7 +371,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
// need recording.
| TerminatorKind::Return
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. } => {}

View file

@ -270,7 +270,7 @@ pub trait ValueAnalysis<'tcx> {
}
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Assert { .. }

View file

@ -104,7 +104,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
for id in calls_to_terminate {
let cleanup = body.basic_blocks_mut()[id].terminator_mut().unwind_mut().unwrap();
*cleanup = UnwindAction::Terminate;
*cleanup = UnwindAction::Terminate(UnwindTerminateReason::Abi);
}
for id in cleanups_to_remove {

View file

@ -53,8 +53,10 @@ impl AddCallGuards {
kind: TerminatorKind::Call { target: Some(ref mut destination), unwind, .. },
source_info,
}) if pred_count[*destination] > 1
&& (matches!(unwind, UnwindAction::Cleanup(_) | UnwindAction::Terminate)
|| self == &AllCallEdges) =>
&& (matches!(
unwind,
UnwindAction::Cleanup(_) | UnwindAction::Terminate(_)
) || self == &AllCallEdges) =>
{
// It's a critical edge, break it
let call_guard = BasicBlockData {

View file

@ -58,7 +58,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
| TerminatorKind::Assert { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::FalseEdge { .. }

View file

@ -679,7 +679,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
// None of these have Operands to const-propagate.
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }

View file

@ -116,7 +116,7 @@ impl CoverageGraph {
match term.kind {
TerminatorKind::Return { .. }
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. } => {
// The `bb` has more than one _outgoing_ edge, or exits the function. Save the

View file

@ -868,7 +868,7 @@ pub(super) fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Sp
// Retain spans from all other terminators
TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop

View file

@ -12,6 +12,7 @@
//! will still not cause any further changes.
//!
use crate::util::is_within_packed;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
@ -49,6 +50,11 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
&& !place.is_indirect()
&& !borrowed.contains(place.local)
&& !state.contains(place.local)
// If `place` is a projection of a disaligned field in a packed ADT,
// the move may be codegened as a pointer to that field.
// Using that disaligned pointer may trigger UB in the callee,
// so do nothing.
&& is_within_packed(tcx, body, place).is_none()
{
call_operands_to_move.push((bb, index));
}

View file

@ -648,7 +648,7 @@ impl WriteInfo {
}
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable { .. } => (),
TerminatorKind::Drop { .. } => {

View file

@ -362,8 +362,13 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
UnwindAction::Unreachable => {
Unwind::To(self.patch.unreachable_cleanup_block())
}
UnwindAction::Terminate => {
Unwind::To(self.patch.terminate_block())
UnwindAction::Terminate(reason) => {
debug_assert_ne!(
reason,
UnwindTerminateReason::InCleanup,
"we are not in a cleanup block, InCleanup reason should be impossible"
);
Unwind::To(self.patch.terminate_block(reason))
}
}
};
@ -496,7 +501,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
if let TerminatorKind::Call {
destination,
target: Some(_),
unwind: UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate,
unwind:
UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate(_),
..
} = data.terminator().kind
{

View file

@ -1091,7 +1091,7 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
UnwindAction::Cleanup(tgt) => tgt,
UnwindAction::Continue => elaborator.patch.resume_block(),
UnwindAction::Unreachable => elaborator.patch.unreachable_cleanup_block(),
UnwindAction::Terminate => elaborator.patch.terminate_block(),
UnwindAction::Terminate(reason) => elaborator.patch.terminate_block(reason),
})
};
elaborate_drop(
@ -1239,7 +1239,7 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
// These never unwind.
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
@ -1759,7 +1759,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
| TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }

View file

@ -906,12 +906,12 @@ impl Integrator<'_, '_> {
UnwindAction::Cleanup(_) | UnwindAction::Continue => {
bug!("cleanup on cleanup block");
}
UnwindAction::Unreachable | UnwindAction::Terminate => return unwind,
UnwindAction::Unreachable | UnwindAction::Terminate(_) => return unwind,
}
}
match unwind {
UnwindAction::Unreachable | UnwindAction::Terminate => unwind,
UnwindAction::Unreachable | UnwindAction::Terminate(_) => unwind,
UnwindAction::Cleanup(target) => UnwindAction::Cleanup(self.map_block(target)),
// Add an unwind edge to the original call's cleanup block
UnwindAction::Continue => self.cleanup_block,
@ -1022,10 +1022,10 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
UnwindAction::Cleanup(tgt) => TerminatorKind::Goto { target: tgt },
UnwindAction::Continue => TerminatorKind::UnwindResume,
UnwindAction::Unreachable => TerminatorKind::Unreachable,
UnwindAction::Terminate => TerminatorKind::UnwindTerminate,
UnwindAction::Terminate(reason) => TerminatorKind::UnwindTerminate(reason),
};
}
TerminatorKind::UnwindTerminate => {}
TerminatorKind::UnwindTerminate(_) => {}
TerminatorKind::Unreachable => {}
TerminatorKind::FalseEdge { ref mut real_target, ref mut imaginary_target } => {
*real_target = self.map_block(*real_target);

View file

@ -72,7 +72,7 @@ impl RemoveNoopLandingPads {
TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
| TerminatorKind::Return
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Unreachable
| TerminatorKind::Call { .. }
| TerminatorKind::Assert { .. }

View file

@ -114,7 +114,7 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
| TerminatorKind::Assert { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::InlineAsm { .. }
@ -166,7 +166,7 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
}
TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop

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