Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7de190a
`io::Error::downcast`: avoid reallocation in case of failure
a1phyr Dec 1, 2025
62f7cf4
move `expr_guaranteed_to_constitute_read_for_never` and `pat_guarante…
WaffleLapkin Dec 2, 2025
784b283
add a test for nested type ascriptions of never type
WaffleLapkin Dec 2, 2025
c5f15bd
fix the check for which expressions read never type
WaffleLapkin Dec 2, 2025
ef27140
Move `tests/ui/{unreachable-code => reachable}`
WaffleLapkin Dec 2, 2025
8c03477
rename cortex-ar references to unified aarch32
robamu Dec 2, 2025
537a8d7
Motor OS: fix compile error
lasiotus Dec 3, 2025
ae93635
compiletest: rename `compile_lib_path` -> `host_compile_lib_path`
jieyouxu Dec 3, 2025
0654444
compiletest: rename `run_lib_path` -> `target_run_lib_path`
jieyouxu Dec 3, 2025
6a00f0d
compiletest: fixup comment for `rustc_path`
jieyouxu Dec 3, 2025
57f9580
compiletest: change some `String`s to `Utf8PathBuf`
jieyouxu Dec 3, 2025
c18cd27
compiletest: flip `nocapture` to `capture`
jieyouxu Dec 3, 2025
bf0970f
add one more test for type ascription of never
WaffleLapkin Dec 2, 2025
5e802dd
moved and deleted test
reddevilmidzy Dec 3, 2025
07b1dad
Added comment to
reddevilmidzy Dec 3, 2025
245096b
Added comment to
reddevilmidzy Dec 3, 2025
cc230fb
Added comment to `tests\ui\str\debug-print-basic-tuple.rs`
reddevilmidzy Dec 3, 2025
d1bc6e3
Merged `tests\ui\type-inference\type-inference-unconstrained-none.rs`
reddevilmidzy Dec 3, 2025
e4f02e4
fix guard patterns interaction with never type
WaffleLapkin Dec 3, 2025
eb03ea4
Revert "implement and test `Iterator::{exactly_one, collect_array}`"
jdonszelmann Dec 3, 2025
c340732
Revert "fixup warnings around the compiler"
jdonszelmann Dec 3, 2025
b54b288
Allow PowerPC spe_acc as clobber-only register
pmur Dec 3, 2025
7e7d724
Implement benchmarks for uN::{gather,scatter}_bits
quaternic Dec 3, 2025
6ce0f0f
Only apply `no_mangle_const_items`'s suggestion to plain const items
reddevilmidzy Dec 2, 2025
2951d72
Simplify and robustly compute suggestion span using
reddevilmidzy Dec 3, 2025
170fffd
Rollup merge of #149521 - a1phyr:improve_io_error, r=joboet
matthiaskrgr Dec 4, 2025
5c9a4ee
Rollup merge of #149544 - reddevilmidzy:where, r=fmease
matthiaskrgr Dec 4, 2025
a411fe8
Rollup merge of #149545 - WaffleLapkin:type_ascribe2, r=jackh726
matthiaskrgr Dec 4, 2025
c3abbc6
Rollup merge of #149570 - robamu:rename-cortex-ar-to-aarch32, r=ehuss
matthiaskrgr Dec 4, 2025
b54ff3d
Rollup merge of #149574 - jieyouxu:compiletest-cli, r=Zalathar
matthiaskrgr Dec 4, 2025
d709cd6
Rollup merge of #149579 - moturus:fix-thread-new, r=joboet
matthiaskrgr Dec 4, 2025
6e8e08c
Rollup merge of #149595 - reddevilmidzy:t9, r=Kivooeo
matthiaskrgr Dec 4, 2025
c598b4e
Rollup merge of #149597 - jdonszelmann:revert-iterator-exactly-one, r…
matthiaskrgr Dec 4, 2025
5c49157
Rollup merge of #149608 - pmur:murp/ppc-clobber-speacc, r=Amanieu
matthiaskrgr Dec 4, 2025
ac607e0
Rollup merge of #149610 - quaternic:gather-scatter-bits-bench, r=Mark…
matthiaskrgr Dec 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions compiler/rustc_codegen_gcc/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,16 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
}
("r", dummy_output_type(self.cx, reg.reg_class()))
} else {
// `clobber_abi` can add lots of clobbers that are not supported by the target,
// such as AVX-512 registers, so we just ignore unsupported registers
let is_target_supported =
reg.reg_class().supported_types(asm_arch, true).iter().any(
let is_target_supported = match reg.reg_class() {
// `clobber_abi` clobbers spe_acc on all PowerPC targets. This
// register is unique to the powerpc*spe target, and the target
// is not supported by gcc. Ignore it.
InlineAsmRegClass::PowerPC(
PowerPCInlineAsmRegClass::spe_acc,
) => false,
// `clobber_abi` can add lots of clobbers that are not supported by the target,
// such as AVX-512 registers, so we just ignore unsupported registers
x => x.supported_types(asm_arch, true).iter().any(
|&(_, feature)| {
if let Some(feature) = feature {
self.tcx
Expand All @@ -222,7 +228,8 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
true // Register class is unconditionally supported
}
},
);
),
};

if is_target_supported && !clobbers.contains(&reg_name) {
clobbers.push(reg_name);
Expand Down Expand Up @@ -710,7 +717,8 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str {
PowerPCInlineAsmRegClass::cr
| PowerPCInlineAsmRegClass::ctr
| PowerPCInlineAsmRegClass::lr
| PowerPCInlineAsmRegClass::xer,
| PowerPCInlineAsmRegClass::xer
| PowerPCInlineAsmRegClass::spe_acc,
) => {
unreachable!("clobber-only")
}
Expand Down Expand Up @@ -793,7 +801,8 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
PowerPCInlineAsmRegClass::cr
| PowerPCInlineAsmRegClass::ctr
| PowerPCInlineAsmRegClass::lr
| PowerPCInlineAsmRegClass::xer,
| PowerPCInlineAsmRegClass::xer
| PowerPCInlineAsmRegClass::spe_acc,
) => {
unreachable!("clobber-only")
}
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_codegen_llvm/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,8 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
PowerPCInlineAsmRegClass::cr
| PowerPCInlineAsmRegClass::ctr
| PowerPCInlineAsmRegClass::lr
| PowerPCInlineAsmRegClass::xer,
| PowerPCInlineAsmRegClass::xer
| PowerPCInlineAsmRegClass::spe_acc,
) => {
unreachable!("clobber-only")
}
Expand Down Expand Up @@ -843,7 +844,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
PowerPCInlineAsmRegClass::cr
| PowerPCInlineAsmRegClass::ctr
| PowerPCInlineAsmRegClass::lr
| PowerPCInlineAsmRegClass::xer,
| PowerPCInlineAsmRegClass::xer
| PowerPCInlineAsmRegClass::spe_acc,
) => {
unreachable!("clobber-only")
}
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ impl MetadataLoader for DefaultMetadataLoader {
format!("failed to parse aix dylib '{}': {}", path.display(), e)
})?;

// FIXME: rewrite in terms of `#![feature(exact_length_collection)]`. See: #149266
match Itertools::exactly_one(archive.members()) {
match archive.members().exactly_one() {
Ok(lib) => {
let lib = lib.map_err(|e| {
format!("failed to parse aix dylib '{}': {}", path.display(), e)
Expand Down
49 changes: 49 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,55 @@ impl<'hir> Pat<'hir> {
});
is_never_pattern
}

/// Whether this pattern constitutes a read of value of the scrutinee that
/// it is matching against. This is used to determine whether we should
/// perform `NeverToAny` coercions.
///
/// See [`expr_guaranteed_to_constitute_read_for_never`][m] for the nuances of
/// what happens when this returns true.
///
/// [m]: ../../rustc_middle/ty/struct.TyCtxt.html#method.expr_guaranteed_to_constitute_read_for_never
pub fn is_guaranteed_to_constitute_read_for_never(&self) -> bool {
match self.kind {
// Does not constitute a read.
PatKind::Wild => false,

// The guard cannot affect if we make a read or not (it runs after the inner pattern
// has matched), therefore it's irrelevant.
PatKind::Guard(pat, _) => pat.is_guaranteed_to_constitute_read_for_never(),

// This is unnecessarily restrictive when the pattern that doesn't
// constitute a read is unreachable.
//
// For example `match *never_ptr { value => {}, _ => {} }` or
// `match *never_ptr { _ if false => {}, value => {} }`.
//
// It is however fine to be restrictive here; only returning `true`
// can lead to unsoundness.
PatKind::Or(subpats) => {
subpats.iter().all(|pat| pat.is_guaranteed_to_constitute_read_for_never())
}

// Does constitute a read, since it is equivalent to a discriminant read.
PatKind::Never => true,

// All of these constitute a read, or match on something that isn't `!`,
// which would require a `NeverToAny` coercion.
PatKind::Missing
| PatKind::Binding(_, _, _, _)
| PatKind::Struct(_, _, _)
| PatKind::TupleStruct(_, _, _)
| PatKind::Tuple(_, _)
| PatKind::Box(_)
| PatKind::Ref(_, _, _)
| PatKind::Deref(_)
| PatKind::Expr(_)
| PatKind::Range(_, _, _)
| PatKind::Slice(_, _, _)
| PatKind::Err(_) => true,
}
}
}

/// A single field in a struct pattern.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self,
cause,
allow_two_phase,
self.expr_guaranteed_to_constitute_read_for_never(expr),
self.tcx.expr_guaranteed_to_constitute_read_for_never(expr),
);
let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;

Expand Down
197 changes: 2 additions & 195 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// While we don't allow *arbitrary* coercions here, we *do* allow
// coercions from ! to `expected`.
if self.try_structurally_resolve_type(expr.span, ty).is_never()
&& self.expr_guaranteed_to_constitute_read_for_never(expr)
&& self.tcx.expr_guaranteed_to_constitute_read_for_never(expr)
{
if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
let reported = self.dcx().span_delayed_bug(
Expand Down Expand Up @@ -320,7 +320,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// diverging would be unsound since we may never actually read the `!`.
// e.g. `let _ = *never_ptr;` with `never_ptr: *const !`.
if self.try_structurally_resolve_type(expr.span, ty).is_never()
&& self.expr_guaranteed_to_constitute_read_for_never(expr)
&& self.tcx.expr_guaranteed_to_constitute_read_for_never(expr)
{
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
}
Expand All @@ -339,199 +339,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty
}

/// Whether this expression constitutes a read of value of the type that
/// it evaluates to.
///
/// This is used to determine if we should consider the block to diverge
/// if the expression evaluates to `!`, and if we should insert a `NeverToAny`
/// coercion for values of type `!`.
///
/// This function generally returns `false` if the expression is a place
/// expression and the *parent* expression is the scrutinee of a match or
/// the pointee of an `&` addr-of expression, since both of those parent
/// expressions take a *place* and not a value.
pub(super) fn expr_guaranteed_to_constitute_read_for_never(
&self,
expr: &'tcx hir::Expr<'tcx>,
) -> bool {
// We only care about place exprs. Anything else returns an immediate
// which would constitute a read. We don't care about distinguishing
// "syntactic" place exprs since if the base of a field projection is
// not a place then it would've been UB to read from it anyways since
// that constitutes a read.
if !expr.is_syntactic_place_expr() {
return true;
}

let parent_node = self.tcx.parent_hir_node(expr.hir_id);
match parent_node {
hir::Node::Expr(parent_expr) => {
match parent_expr.kind {
// Addr-of, field projections, and LHS of assignment don't constitute reads.
// Assignment does call `drop_in_place`, though, but its safety
// requirements are not the same.
ExprKind::AddrOf(..) | hir::ExprKind::Field(..) => false,

// Place-preserving expressions only constitute reads if their
// parent expression constitutes a read.
ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) => {
self.expr_guaranteed_to_constitute_read_for_never(expr)
}

ExprKind::Assign(lhs, _, _) => {
// Only the LHS does not constitute a read
expr.hir_id != lhs.hir_id
}

// See note on `PatKind::Or` below for why this is `all`.
ExprKind::Match(scrutinee, arms, _) => {
assert_eq!(scrutinee.hir_id, expr.hir_id);
arms.iter()
.all(|arm| self.pat_guaranteed_to_constitute_read_for_never(arm.pat))
}
ExprKind::Let(hir::LetExpr { init, pat, .. }) => {
assert_eq!(init.hir_id, expr.hir_id);
self.pat_guaranteed_to_constitute_read_for_never(*pat)
}

// Any expression child of these expressions constitute reads.
ExprKind::Array(_)
| ExprKind::Call(_, _)
| ExprKind::Use(_, _)
| ExprKind::MethodCall(_, _, _, _)
| ExprKind::Tup(_)
| ExprKind::Binary(_, _, _)
| ExprKind::Unary(_, _)
| ExprKind::Cast(_, _)
| ExprKind::DropTemps(_)
| ExprKind::If(_, _, _)
| ExprKind::Closure(_)
| ExprKind::Block(_, _)
| ExprKind::AssignOp(_, _, _)
| ExprKind::Index(_, _, _)
| ExprKind::Break(_, _)
| ExprKind::Ret(_)
| ExprKind::Become(_)
| ExprKind::InlineAsm(_)
| ExprKind::Struct(_, _, _)
| ExprKind::Repeat(_, _)
| ExprKind::Yield(_, _) => true,

// These expressions have no (direct) sub-exprs.
ExprKind::ConstBlock(_)
| ExprKind::Loop(_, _, _, _)
| ExprKind::Lit(_)
| ExprKind::Path(_)
| ExprKind::Continue(_)
| ExprKind::OffsetOf(_, _)
| ExprKind::Err(_) => unreachable!("no sub-expr expected for {:?}", expr.kind),
}
}

// If we have a subpattern that performs a read, we want to consider this
// to diverge for compatibility to support something like `let x: () = *never_ptr;`.
hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) => {
assert_eq!(target.hir_id, expr.hir_id);
self.pat_guaranteed_to_constitute_read_for_never(*pat)
}

// These nodes (if they have a sub-expr) do constitute a read.
hir::Node::Block(_)
| hir::Node::Arm(_)
| hir::Node::ExprField(_)
| hir::Node::AnonConst(_)
| hir::Node::ConstBlock(_)
| hir::Node::ConstArg(_)
| hir::Node::Stmt(_)
| hir::Node::Item(hir::Item {
kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..),
..
})
| hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Const(..), ..
})
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => true,

hir::Node::TyPat(_) | hir::Node::Pat(_) => {
self.dcx().span_delayed_bug(expr.span, "place expr not allowed in pattern");
true
}

// These nodes do not have direct sub-exprs.
hir::Node::Param(_)
| hir::Node::Item(_)
| hir::Node::ForeignItem(_)
| hir::Node::TraitItem(_)
| hir::Node::ImplItem(_)
| hir::Node::Variant(_)
| hir::Node::Field(_)
| hir::Node::PathSegment(_)
| hir::Node::Ty(_)
| hir::Node::AssocItemConstraint(_)
| hir::Node::TraitRef(_)
| hir::Node::PatField(_)
| hir::Node::PatExpr(_)
| hir::Node::LetStmt(_)
| hir::Node::Synthetic
| hir::Node::Err(_)
| hir::Node::Ctor(_)
| hir::Node::Lifetime(_)
| hir::Node::GenericParam(_)
| hir::Node::Crate(_)
| hir::Node::Infer(_)
| hir::Node::WherePredicate(_)
| hir::Node::PreciseCapturingNonLifetimeArg(_)
| hir::Node::OpaqueTy(_) => {
unreachable!("no sub-expr expected for {parent_node:?}")
}
}
}

/// Whether this pattern constitutes a read of value of the scrutinee that
/// it is matching against. This is used to determine whether we should
/// perform `NeverToAny` coercions.
///
/// See above for the nuances of what happens when this returns true.
pub(super) fn pat_guaranteed_to_constitute_read_for_never(&self, pat: &hir::Pat<'_>) -> bool {
match pat.kind {
// Does not constitute a read.
hir::PatKind::Wild => false,

// Might not constitute a read, since the condition might be false.
hir::PatKind::Guard(_, _) => true,

// This is unnecessarily restrictive when the pattern that doesn't
// constitute a read is unreachable.
//
// For example `match *never_ptr { value => {}, _ => {} }` or
// `match *never_ptr { _ if false => {}, value => {} }`.
//
// It is however fine to be restrictive here; only returning `true`
// can lead to unsoundness.
hir::PatKind::Or(subpats) => {
subpats.iter().all(|pat| self.pat_guaranteed_to_constitute_read_for_never(pat))
}

// Does constitute a read, since it is equivalent to a discriminant read.
hir::PatKind::Never => true,

// All of these constitute a read, or match on something that isn't `!`,
// which would require a `NeverToAny` coercion.
hir::PatKind::Missing
| hir::PatKind::Binding(_, _, _, _)
| hir::PatKind::Struct(_, _, _)
| hir::PatKind::TupleStruct(_, _, _)
| hir::PatKind::Tuple(_, _)
| hir::PatKind::Box(_)
| hir::PatKind::Ref(_, _, _)
| hir::PatKind::Deref(_)
| hir::PatKind::Expr(_)
| hir::PatKind::Range(_, _, _)
| hir::PatKind::Slice(_, _, _)
| hir::PatKind::Err(_) => true,
}
}

#[instrument(skip(self, expr), level = "debug")]
fn check_expr_kind(
&self,
Expand Down
21 changes: 9 additions & 12 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub use rustc_session::lint::builtin::*;
use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
use rustc_span::{BytePos, DUMMY_SP, Ident, InnerSpan, Span, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, Ident, InnerSpan, Span, Symbol, kw, sym};
use rustc_target::asm::InlineAsmArch;
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy;
Expand Down Expand Up @@ -997,18 +997,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
self.check_no_mangle_on_generic_fn(cx, attr_span, it.owner_id.def_id);
}
}
hir::ItemKind::Const(..) => {
hir::ItemKind::Const(ident, generics, ..) => {
if find_attr!(attrs, AttributeKind::NoMangle(..)) {
// account for "pub const" (#45562)
let start = cx
.tcx
.sess
.source_map()
.span_to_snippet(it.span)
.map(|snippet| snippet.find("const").unwrap_or(0))
.unwrap_or(0) as u32;
// `const` is 5 chars
let suggestion = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
let suggestion =
if generics.params.is_empty() && generics.where_clause_span.is_empty() {
// account for "pub const" (#45562)
Some(it.span.until(ident.span))
} else {
None
};

// Const items do not refer to a particular location in memory, and therefore
// don't have anything to attach a symbol to
Expand Down
Loading
Loading