Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 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
1e6b444
library: fs: Factor out a `file_time_to_timespec` function in prepara…
joshtriplett Oct 7, 2025
1bf555c
library: fs: Factor out the Apple file time to attrlist code for reuse
joshtriplett Oct 7, 2025
0355358
run zero-size assertion in `const {}`
cyrgani Oct 8, 2025
e3d2016
inline constants in generated `enum` `Encode` impls
cyrgani Oct 8, 2025
7225454
Implement fs api set_times and set_times_nofollow
chenyukang Oct 8, 2025
3d40fa6
Update library/std/src/fs.rs
chenyukang Oct 9, 2025
6308e76
Update library/std/src/fs.rs
chenyukang Oct 9, 2025
1c5c8ca
use proper unsupported
chenyukang Oct 9, 2025
46c6f0a
rebase #147504
chenyukang Oct 9, 2025
2438df7
support fs::set_times for wasi
chenyukang Oct 9, 2025
1dd5641
add doc alias for set_times_nofollow
chenyukang Oct 9, 2025
6fd1c2b
add doc alias for set_times
chenyukang Oct 9, 2025
901366a
fix c_char error in Android
chenyukang Oct 9, 2025
f8118d8
unsupported: Use `unsupported()` for `set_times`
joshtriplett Oct 9, 2025
d2f590a
unsupported: Use `unsupported()` for `set_times_nofollow`
joshtriplett 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
87ac767
Rollup merge of #146277 - Kivooeo:u64-unlock, r=scottmcm
Zalathar Oct 14, 2025
d57f99d
Rollup merge of #146976 - npmccallum:clone, r=scottmcm
Zalathar Oct 14, 2025
bb460c2
Rollup merge of #147249 - jackh726:opaque-type-fallback, r=lcnr
Zalathar Oct 14, 2025
85ef06c
Rollup merge of #147266 - lcnr:fix-search_graph, r=BoxyUwU
Zalathar Oct 14, 2025
0adccf9
Rollup merge of #147468 - chenyukang:yukang-api-set-times, r=joshtrip…
Zalathar Oct 14, 2025
ebbb223
Rollup merge of #147497 - cyrgani:proc-macro-cleanups-3, r=petrochenkov
Zalathar Oct 14, 2025
e5d4915
Rollup merge of #147594 - vexide:vexos-exit-impl, r=joboet
Zalathar Oct 14, 2025
cccca62
Rollup merge of #147596 - thejpster:build-new-arm-tier2-targets, r=Ma…
Zalathar Oct 14, 2025
23c518d
Rollup merge of #147607 - dianqk:gvn-deref-loop, r=cjgillot
Zalathar Oct 14, 2025
d92dead
Rollup merge of #147620 - saethlin:RangeFrom-noubcheck, r=scottmcm
Zalathar Oct 14, 2025
304b76d
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
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
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
}
14 changes: 9 additions & 5 deletions compiler/rustc_next_trait_solver/src/solve/search_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,23 @@ where
response_no_constraints(cx, input, Certainty::overflow(false))
}

fn is_ambiguous_result(result: QueryResult<I>) -> bool {
result.is_ok_and(|response| {
has_no_inference_or_external_constraints(response)
fn is_ambiguous_result(result: QueryResult<I>) -> Option<Certainty> {
result.ok().and_then(|response| {
if has_no_inference_or_external_constraints(response)
&& matches!(response.value.certainty, Certainty::Maybe { .. })
{
Some(response.value.certainty)
} else {
None
}
})
}

fn propagate_ambiguity(
cx: I,
for_input: CanonicalInput<I>,
from_result: QueryResult<I>,
certainty: Certainty,
) -> QueryResult<I> {
let certainty = from_result.unwrap().value.certainty;
response_no_constraints(cx, for_input, certainty)
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::inherent::*;
use crate::ir_print::IrPrint;
use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
use crate::relate::Relate;
use crate::solve::{CanonicalInput, ExternalConstraintsData, QueryResult, inspect};
use crate::solve::{CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect};
use crate::visit::{Flags, TypeVisitable};
use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph};

Expand Down Expand Up @@ -550,6 +550,7 @@ impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
impl<I: Interner> search_graph::Cx for I {
type Input = CanonicalInput<I>;
type Result = QueryResult<I>;
type AmbiguityInfo = Certainty;

type DepNodeIndex = I::DepNodeIndex;
type Tracked<T: Debug + Clone> = I::Tracked<T>;
Expand Down
Loading
Loading