Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
13ed4a3
uncommented u64 impl
Kivooeo Sep 6, 2025
1e772f9
check for empty heads after adding parent
lcnr Oct 2, 2025
36283bc
fix `RebaseReason::Ambiguity`
lcnr Oct 2, 2025
db3c9b6
When computing opaque types in the next solver, take an initial pass …
jackh726 Sep 30, 2025
9a5cef1
Cleanup opaque_type computation a bit
jackh726 Oct 1, 2025
c6c58e3
Remove UsageKind::HasError
jackh726 Oct 1, 2025
a3fbae5
Merge match and if
jackh726 Oct 1, 2025
283ad66
Fix comment, change first_pass to error_on_missing_defining_use, remo…
jackh726 Oct 3, 2025
8b17827
Remove unneeded get_hidden_type
jackh726 Oct 5, 2025
04c2724
constify basic Clone impls
npmccallum Sep 13, 2025
0355358
run zero-size assertion in `const {}`
cyrgani Oct 8, 2025
e3d2016
inline constants in generated `enum` `Encode` impls
cyrgani Oct 8, 2025
ca8020a
Fix ice 141592, change diverge assertion to struct error with delay
chenyukang Oct 9, 2025
6a12470
rename `DecodeMut` to `Decode`
cyrgani Oct 10, 2025
04da682
vexos: implement `pal::os::exit`
tropicaaal Oct 11, 2025
cc96d3f
Adjust the Arm targets in CI to reflect latest changes
thejpster Oct 11, 2025
64c023b
Add miscompiled test cases
dianqk Oct 12, 2025
9b91c3e
Add CFLAGS for armv8r-none-eabihf
thejpster Oct 12, 2025
fef16d2
Change armv7a-none-eabihf CFLAGS to assume only single-precision FPU
thejpster Oct 12, 2025
4f1b945
Avoid redundant UB check in RangeFrom slice indexing
saethlin Oct 12, 2025
11977b2
Hide vendoring and copyright in GHA group
Noratrieb Oct 13, 2025
d51f09e
Review comments. Move resolve_vars_if_possible into fn and add test m…
jackh726 Oct 12, 2025
2048b9c
GVN: Invalidate derefs at loop headers
dianqk Oct 12, 2025
d658bcf
Suppress unused_parens for labeled break
chenyukang Oct 10, 2025
6e07b25
Rollup merge of #146277 - Kivooeo:u64-unlock, r=scottmcm
Zalathar Oct 14, 2025
ed290fd
Rollup merge of #146976 - npmccallum:clone, r=scottmcm
Zalathar Oct 14, 2025
3a80521
Rollup merge of #147249 - jackh726:opaque-type-fallback, r=lcnr
Zalathar Oct 14, 2025
4321adf
Rollup merge of #147266 - lcnr:fix-search_graph, r=BoxyUwU
Zalathar Oct 14, 2025
428be2f
Rollup merge of #147497 - cyrgani:proc-macro-cleanups-3, r=petrochenkov
Zalathar Oct 14, 2025
a49246a
Rollup merge of #147546 - chenyukang:yukang-fix-break-label-147542, r…
Zalathar Oct 14, 2025
0a7f159
Rollup merge of #147548 - chenyukang:yukang-fix-ice-141592, r=jackh726
Zalathar Oct 14, 2025
a594163
Rollup merge of #147594 - vexide:vexos-exit-impl, r=joboet
Zalathar Oct 14, 2025
2ad6152
Rollup merge of #147596 - thejpster:build-new-arm-tier2-targets, r=Ma…
Zalathar Oct 14, 2025
4fc3a05
Rollup merge of #147607 - dianqk:gvn-deref-loop, r=cjgillot
Zalathar Oct 14, 2025
4a67c01
Rollup merge of #147620 - saethlin:RangeFrom-noubcheck, r=scottmcm
Zalathar Oct 14, 2025
0f6fe91
Rollup merge of #147647 - Noratrieb:bootstrap-groups, r=Zalathar
Zalathar Oct 14, 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
10 changes: 2 additions & 8 deletions compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,6 @@ fn add_hidden_type<'tcx>(
}
}

fn get_hidden_type<'tcx>(
hidden_types: &DefinitionSiteHiddenTypes<'tcx>,
def_id: LocalDefId,
) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
hidden_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty))
}

#[derive(Debug)]
struct DefiningUse<'tcx> {
/// The opaque type using non NLL vars. This uses the actual
Expand Down Expand Up @@ -508,7 +501,8 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>(
let tcx = infcx.tcx;
let mut errors = Vec::new();
for &(key, hidden_type) in opaque_types {
let Some(expected) = get_hidden_type(hidden_types, key.def_id) else {
let Some(expected) = hidden_types.0.get(&key.def_id).map(|ty| EarlyBinder::bind(*ty))
else {
if !tcx.use_typing_mode_borrowck() {
if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
&& alias_ty.def_id == key.def_id.to_def_id()
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1817,12 +1817,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
let element_ty = if !args.is_empty() {
// This shouldn't happen unless there's another error
// (e.g., never patterns in inappropriate contexts).
if self.diverges.get() != Diverges::Maybe {
self.dcx()
.struct_span_err(expr.span, "unexpected divergence state in checking array")
.delay_as_bug();
}

let coerce_to = expected
.to_option(self)
.and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index())
.unwrap_or_else(|| self.next_ty_var(expr.span));
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
assert_eq!(self.diverges.get(), Diverges::Maybe);

for e in args {
let e_ty = self.check_expr_with_hint(e, coerce_to);
let cause = self.misc(e.span);
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ pub(crate) struct FnCtxt<'a, 'tcx> {
/// the diverges flag is set to something other than `Maybe`.
pub(super) diverges: Cell<Diverges>,

/// If one of the function arguments is a never pattern, this counts as diverging code. This
/// affect typechecking of the function body.
/// If one of the function arguments is a never pattern, this counts as diverging code.
/// This affect typechecking of the function body.
pub(super) function_diverges_because_of_empty_arguments: Cell<Diverges>,

/// Whether the currently checked node is the whole body of the function.
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ fn typeck_with_inspect<'tcx>(
// the future.
fcx.check_repeat_exprs();

// We need to handle opaque types before emitting ambiguity errors as applying
// defining uses may guide type inference.
if fcx.next_trait_solver() {
fcx.try_handle_opaque_type_uses_next();
}

fcx.type_inference_fallback();

// Even though coercion casts provide type hints, we check casts after fallback for
Expand Down
107 changes: 66 additions & 41 deletions compiler/rustc_hir_typeck/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,50 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
/// inference variables.
///
/// It then uses these defining uses to guide inference for all other uses.
///
/// Unlike `handle_opaque_type_uses_next`, this does not report errors.
#[instrument(level = "debug", skip(self))]
pub(super) fn try_handle_opaque_type_uses_next(&mut self) {
// We clone the opaques instead of stealing them here as we still need
// to use them after fallback.
let opaque_types: Vec<_> = self.infcx.clone_opaque_types();

self.compute_definition_site_hidden_types(opaque_types, false);
}

/// This takes all the opaque type uses during HIR typeck. It first computes
/// the concrete hidden type by iterating over all defining uses.
///
/// A use during HIR typeck is defining if all non-lifetime arguments are
/// unique generic parameters and the hidden type does not reference any
/// inference variables.
///
/// It then uses these defining uses to guide inference for all other uses.
#[instrument(level = "debug", skip(self))]
pub(super) fn handle_opaque_type_uses_next(&mut self) {
// We clone the opaques instead of stealing them here as they are still used for
// normalization in the next generation trait solver.
let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types();
let opaque_types: Vec<_> = self.infcx.clone_opaque_types();
let num_entries = self.inner.borrow_mut().opaque_types().num_entries();
let prev = self.checked_opaque_types_storage_entries.replace(Some(num_entries));
debug_assert_eq!(prev, None);
for entry in &mut opaque_types {
*entry = self.resolve_vars_if_possible(*entry);
}
debug!(?opaque_types);

self.compute_definition_site_hidden_types(&opaque_types);
self.apply_definition_site_hidden_types(&opaque_types);
self.compute_definition_site_hidden_types(opaque_types, true);
}
}

#[derive(Copy, Clone, Debug)]
enum UsageKind<'tcx> {
None,
NonDefiningUse(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>),
UnconstrainedHiddenType(OpaqueHiddenType<'tcx>),
HasDefiningUse,
HasDefiningUse(OpaqueHiddenType<'tcx>),
}

impl<'tcx> UsageKind<'tcx> {
fn merge(&mut self, other: UsageKind<'tcx>) {
match (&*self, &other) {
(UsageKind::HasDefiningUse, _) | (_, UsageKind::None) => unreachable!(),
(UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(),
(UsageKind::None, _) => *self = other,
// When mergining non-defining uses, prefer earlier ones. This means
// the error happens as early as possible.
Expand All @@ -64,7 +79,7 @@ impl<'tcx> UsageKind<'tcx> {
// intended to be defining.
(
UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse,
UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse(_),
) => *self = other,
}
}
Expand All @@ -73,8 +88,14 @@ impl<'tcx> UsageKind<'tcx> {
impl<'tcx> FnCtxt<'_, 'tcx> {
fn compute_definition_site_hidden_types(
&mut self,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
mut opaque_types: Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>,
error_on_missing_defining_use: bool,
) {
for entry in opaque_types.iter_mut() {
*entry = self.resolve_vars_if_possible(*entry);
}
debug!(?opaque_types);

let tcx = self.tcx;
let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode()
else {
Expand All @@ -88,19 +109,47 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
_ => unreachable!("not opaque or generator: {def_id:?}"),
}

// We do actually need to check this the second pass (we can't just
// store this), because we can go from `UnconstrainedHiddenType` to
// `HasDefiningUse` (because of fallback)
let mut usage_kind = UsageKind::None;
for &(opaque_type_key, hidden_type) in opaque_types {
for &(opaque_type_key, hidden_type) in &opaque_types {
if opaque_type_key.def_id != def_id {
continue;
}

usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type));
if let UsageKind::HasDefiningUse = usage_kind {

if let UsageKind::HasDefiningUse(..) = usage_kind {
break;
}
}

if let UsageKind::HasDefiningUse(ty) = usage_kind {
for &(opaque_type_key, hidden_type) in &opaque_types {
if opaque_type_key.def_id != def_id {
continue;
}

let expected = EarlyBinder::bind(ty.ty).instantiate(tcx, opaque_type_key.args);
self.demand_eqtype(hidden_type.span, expected, hidden_type.ty);
}

// Being explicit here: it may be possible that we in a
// previous call to this function we did an insert, but this
// should be just fine, since they all get equated anyways and
// we shouldn't ever go from `HasDefiningUse` to anyway else.
let _ = self.typeck_results.borrow_mut().hidden_types.insert(def_id, ty);
}

// If we're in `fn try_handle_opaque_type_uses_next` then do not
// report any errors.
if !error_on_missing_defining_use {
continue;
}

let guar = match usage_kind {
UsageKind::HasDefiningUse(_) => continue,
UsageKind::None => {
if let Some(guar) = self.tainted_by_errors() {
guar
Expand Down Expand Up @@ -137,7 +186,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
.emit()
}
}
UsageKind::HasDefiningUse => continue,
};

self.typeck_results
Expand All @@ -148,8 +196,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
}
}

#[tracing::instrument(skip(self), ret)]
fn consider_opaque_type_use(
&mut self,
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
hidden_type: OpaqueHiddenType<'tcx>,
) -> UsageKind<'tcx> {
Expand All @@ -161,11 +210,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
) {
match err {
NonDefiningUseReason::Tainted(guar) => {
self.typeck_results.borrow_mut().hidden_types.insert(
opaque_type_key.def_id,
OpaqueHiddenType::new_error(self.tcx, guar),
);
return UsageKind::HasDefiningUse;
return UsageKind::HasDefiningUse(OpaqueHiddenType::new_error(self.tcx, guar));
}
_ => return UsageKind::NonDefiningUse(opaque_type_key, hidden_type),
};
Expand Down Expand Up @@ -193,27 +238,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
self.tcx,
DefiningScopeKind::HirTypeck,
);

let prev = self
.typeck_results
.borrow_mut()
.hidden_types
.insert(opaque_type_key.def_id, hidden_type);
assert!(prev.is_none());
UsageKind::HasDefiningUse
}

fn apply_definition_site_hidden_types(
&mut self,
opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
) {
let tcx = self.tcx;
for &(key, hidden_type) in opaque_types {
let expected = *self.typeck_results.borrow_mut().hidden_types.get(&key.def_id).unwrap();

let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args);
self.demand_eqtype(hidden_type.span, expected, hidden_type.ty);
}
UsageKind::HasDefiningUse(hidden_type)
}

/// We may in theory add further uses of an opaque after cloning the opaque
Expand Down
11 changes: 10 additions & 1 deletion compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,16 @@ trait UnusedDelimLint {
(value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
}

Break(_, Some(ref value)) => {
Break(label, Some(ref value)) => {
// Don't lint on `break 'label ({...})` - the parens are necessary
// to disambiguate from `break 'label {...}` which would be a syntax error.
// This avoids conflicts with the `break_with_label_and_loop` lint.
if label.is_some()
&& matches!(value.kind, ast::ExprKind::Paren(ref inner)
if matches!(inner.kind, ast::ExprKind::Block(..)))
{
return;
}
(value, UnusedDelimsCtx::BreakValue, false, None, None, true)
}

Expand Down
29 changes: 29 additions & 0 deletions compiler/rustc_middle/src/mir/loops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use rustc_index::bit_set::DenseBitSet;

use super::*;

/// Compute the set of loop headers in the given body. A loop header is usually defined as a block
/// which dominates one of its predecessors. This definition is only correct for reducible CFGs.
/// However, computing dominators is expensive, so we approximate according to the post-order
/// traversal order. A loop header for us is a block which is visited after its predecessor in
/// post-order. This is ok as we mostly need a heuristic.
pub fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> {
let mut maybe_loop_headers = DenseBitSet::new_empty(body.basic_blocks.len());
let mut visited = DenseBitSet::new_empty(body.basic_blocks.len());
for (bb, bbdata) in traversal::postorder(body) {
// Post-order means we visit successors before the block for acyclic CFGs.
// If the successor is not visited yet, consider it a loop header.
for succ in bbdata.terminator().successors() {
if !visited.contains(succ) {
maybe_loop_headers.insert(succ);
}
}

// Only mark `bb` as visited after we checked the successors, in case we have a self-loop.
// bb1: goto -> bb1;
let _new = visited.insert(bb);
debug_assert!(_new);
}

maybe_loop_headers
}
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mod statement;
mod syntax;
mod terminator;

pub mod loops;
pub mod traversal;
pub mod visit;

Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
let ssa = SsaLocals::new(tcx, body, typing_env);
// Clone dominators because we need them while mutating the body.
let dominators = body.basic_blocks.dominators().clone();
let maybe_loop_headers = loops::maybe_loop_headers(body);

let arena = DroplessArena::default();
let mut state =
Expand All @@ -141,6 +142,11 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {

let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
for bb in reverse_postorder {
// N.B. With loops, reverse postorder cannot produce a valid topological order.
// A statement or terminator from inside the loop, that is not processed yet, may have performed an indirect write.
if maybe_loop_headers.contains(bb) {
state.invalidate_derefs();
}
let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
state.visit_basic_block_data(bb, data);
}
Expand Down
28 changes: 1 addition & 27 deletions compiler/rustc_mir_transform/src/jump_threading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
body,
arena,
map: Map::new(tcx, body, Some(MAX_PLACES)),
maybe_loop_headers: maybe_loop_headers(body),
maybe_loop_headers: loops::maybe_loop_headers(body),
opportunities: Vec::new(),
};

Expand Down Expand Up @@ -830,29 +830,3 @@ enum Update {
Incr,
Decr,
}

/// Compute the set of loop headers in the given body. A loop header is usually defined as a block
/// which dominates one of its predecessors. This definition is only correct for reducible CFGs.
/// However, computing dominators is expensive, so we approximate according to the post-order
/// traversal order. A loop header for us is a block which is visited after its predecessor in
/// post-order. This is ok as we mostly need a heuristic.
fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> {
let mut maybe_loop_headers = DenseBitSet::new_empty(body.basic_blocks.len());
let mut visited = DenseBitSet::new_empty(body.basic_blocks.len());
for (bb, bbdata) in traversal::postorder(body) {
// Post-order means we visit successors before the block for acyclic CFGs.
// If the successor is not visited yet, consider it a loop header.
for succ in bbdata.terminator().successors() {
if !visited.contains(succ) {
maybe_loop_headers.insert(succ);
}
}

// Only mark `bb` as visited after we checked the successors, in case we have a self-loop.
// bb1: goto -> bb1;
let _new = visited.insert(bb);
debug_assert!(_new);
}

maybe_loop_headers
}
Loading
Loading