Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c1c3daa
Uplift flatten_answer_tree to next-solver
lsunsi Oct 7, 2026
61d4201
Add test for transmute recycle on next solver
lsunsi Oct 6, 2026
01d70ab
Update is_transmutable on next solver to consider nested goals
lsunsi Oct 7, 2026
247b4de
Update test to reflect previous fix
lsunsi Oct 7, 2026
5eb32c9
tidy up!
lsunsi Oct 7, 2026
b6fecaf
Replace require_lang_item with require_trait_lang_item (avoiding dumm…
lsunsi Oct 7, 2026
da48483
Avoid needless collect
lsunsi Oct 7, 2026
1266bc0
Add a couple more tests
lsunsi Oct 7, 2026
d995a73
Make flatten_answer_tree Interner generic
lsunsi Oct 7, 2026
0f6564e
Remove middle from next-solver (sorry)
lsunsi Oct 8, 2026
372a9e9
Remove needless param: assume
lsunsi Oct 9, 2026
21b29a6
is_transmutable returns answer now
lsunsi Oct 9, 2026
5b216e0
Uplift transmute Answer/Condition into type-ir
lsunsi Oct 9, 2026
ae3df03
Make transmute test suite next-solver
lsunsi Oct 9, 2026
0f13998
Fix test imports for moved transmute
lsunsi Oct 9, 2026
2c21fc5
Add test that checks add_goals for flattened goals
lsunsi Oct 9, 2026
cf94b14
Mark fixed transmutability tests as fixed
lsunsi Oct 10, 2026
10d2bb9
Fix an needless import and needless full path
lsunsi Oct 10, 2026
7c75468
Add revision to a couple of tests
lsunsi Oct 10, 2026
71f84bd
Fix a couple of type references in doc (what was I thinking)
lsunsi Oct 10, 2026
3e780c4
Add revisions to all previously edited tests
lsunsi Oct 10, 2026
7c32373
Remove couple more unused imports
lsunsi Oct 12, 2026
2cee0dc
trait-system-refactor-initiative#152 is not a bug anymore
lsunsi Oct 12, 2026
13694b6
Drop two repeated tests and an unecessary directive
lsunsi Oct 12, 2026
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
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ bidirectional_lang_item_map! {
FnMut,
FnOnce,
FnPtrTrait,
Freeze,
FusedIterator,
Future,
Iterator,
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_next_trait_solver/src/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ use std::fmt::Debug;
use std::ops::Deref;

use rustc_type_ir::solve::{
Certainty, ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution,
ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution,
VisibleForLeakCheck,
};
use rustc_type_ir::{self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, TypeFoldable};
use rustc_type_ir::transmute::Answer;
use rustc_type_ir::{
self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, Region, TypeFoldable,
};

/// `SolverDelegate` is one of the two traits in the `rustc_type_ir` shared abstraction layer
/// between rustc and rust-analyzer abstracting over the [InferCtxt][inferctxt-doc], which had to be
Expand Down Expand Up @@ -113,7 +116,7 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
src: <Self::Interner as Interner>::Ty,
dst: <Self::Interner as Interner>::Ty,
assume: Const<Self::Interner>,
) -> Result<Certainty, NoSolution>;
) -> Result<Answer<Region<Self::Interner>, <Self::Interner as Interner>::Ty>, NoSolution>;

/// Obtain canonicalizer state, either by allocating it afresh (the default) or by reusing
/// previously allocated state.
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rustc_type_ir::solve::{
NoSolutionOrRerunNonErased, OpaqueTypesJank, QueryResultOrRerunNonErased, RerunCondition,
RerunNonErased, RerunReason, RerunResultExt, SmallCopySet, TyOrConstInferVar,
};
use rustc_type_ir::transmute::Answer;
use rustc_type_ir::{
self as ty, ClauseKind, Const, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey,
PredicateKind, PredicateProxy, Region, RegionVid, TypeFoldable, TypeSuperVisitable,
Expand Down Expand Up @@ -1565,7 +1566,7 @@ where
src: I::Ty,
dst: I::Ty,
assume: Const<I>,
) -> Result<Certainty, NoSolution> {
) -> Result<Answer<Region<I>, I::Ty>, NoSolution> {
self.delegate.is_transmutable(dst, src, assume)
}

Expand Down
44 changes: 43 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ mod trait_goals;

use derive_where::derive_where;
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::SolverTraitLangItem;
pub use rustc_type_ir::solve::*;
use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt};
use rustc_type_ir::transmute::Condition;
use rustc_type_ir::{self as ty, Const, Interner, Region, TraitClause, TypeVisitableExt, Upcast};
use thin_vec::{ThinVec, thin_vec};
use tracing::instrument;

pub use self::eval_ctxt::{
Expand Down Expand Up @@ -491,3 +494,42 @@ pub struct GoalEvaluation<I: Interner> {
/// before rerunning it.
pub stalled_on: Option<GoalStalledOn<I>>,
}

/// Flatten the `Condition` tree into a conjunction of predicates.
#[instrument(level = "debug", skip(interner, predicate))]
pub fn flatten_answer_tree<I: Interner>(
interner: I,
predicate: TraitClause<I>,
cond: Condition<Region<I>, I::Ty>,
) -> ThinVec<I::Predicate> {
match cond {
// FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
// Not possible until the trait solver supports disjunctions of obligations
Condition::IfAll(conds) | Condition::IfAny(conds) => conds
.into_iter()
.flat_map(|cond| flatten_answer_tree(interner, predicate, cond))
.collect(),
Condition::Immutable { ty } => {
let trait_ref = ty::TraitRef::new(
interner,
interner.require_trait_lang_item(SolverTraitLangItem::Freeze),
[I::GenericArg::from(ty)],
);
thin_vec![trait_ref.upcast(interner)]
}
Condition::Outlives { long, short } => {
let outlives = ty::OutlivesClause(long, short);
thin_vec![outlives.upcast(interner)]
}
Condition::Transmutable { src, dst } => {
let transmute_trait = predicate.def_id();
Comment thread
lsunsi marked this conversation as resolved.
Resolved
let assume = predicate.trait_ref.args.const_at(2);
let trait_ref = ty::TraitRef::new(
interner,
transmute_trait,
[I::GenericArg::from(dst), I::GenericArg::from(src), I::GenericArg::from(assume)],
);
thin_vec![trait_ref.upcast(interner)]
}
}
}
23 changes: 21 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::solve::assembly::{
use crate::solve::inspect::ProbeKind;
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines,
MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines, flatten_answer_tree,
has_only_region_constraints,
};

Expand Down Expand Up @@ -656,6 +656,8 @@ where
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
use rustc_type_ir::transmute::Answer;

if goal.predicate.polarity != ty::ClausePolarity::Positive {
return Err(NoSolution.into());
}
Expand All @@ -678,11 +680,28 @@ where
goal.predicate.trait_ref.args.const_at(2),
)?;

let certainty = ecx.is_transmutable(
let answer = ecx.is_transmutable(
goal.predicate.trait_ref.args.type_at(0),
goal.predicate.trait_ref.args.type_at(1),
assume,
)?;

let certainty = match answer {
Answer::No(_) => return Err(NoSolution.into()),
Answer::Yes => Certainty::Yes,
Answer::If(cond) => {
let tcx = ecx.cx();
ecx.add_goals(
GoalSource::Misc,
flatten_answer_tree(tcx, goal.predicate, cond)
.into_iter()
.map(|predicate| Goal::new(tcx, goal.param_env, predicate)),
)?;

Certainty::Yes
}
};

ecx.evaluate_added_goals_and_make_canonical_response(certainty)
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use rustc_middle::ty::print::{
PrintPolyTraitClauseExt, PrintPolyTraitRefExt as _, PrintTraitClauseExt as _,
PrintTraitRefExt as _, with_forced_trimmed_paths,
};
use rustc_middle::ty::transmute::{Answer, Reason};
use rustc_middle::ty::{
self, GenericArgKind, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder,
TypeSuperFoldable, TypeVisitableExt, Unnormalized, Upcast,
Expand Down Expand Up @@ -3253,7 +3254,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_ref.args.type_at(0),
assume,
),
rustc_transmute::Answer::Yes,
Answer::Yes,
);

// If the normalized check unexpectedly passes, fall back to root obligation for reporting.
Expand All @@ -3274,7 +3275,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_pred: ty::PolyTraitClause<'tcx>,
span: Span,
) -> GetSafeTransmuteErrorAndReason {
use rustc_transmute::Answer;
self.probe(|_| {
// We don't assemble a transmutability candidate for types that are generic
// and we should have ambiguity for types that still have non-region infer.
Expand Down Expand Up @@ -3311,69 +3311,61 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
{
Answer::No(reason) => {
let safe_transmute_explanation = match reason {
rustc_transmute::Reason::SrcIsNotYetSupported => {
Reason::SrcIsNotYetSupported => {
format!("analyzing the transmutability of `{src}` is not yet supported")
}
rustc_transmute::Reason::DstIsNotYetSupported => {
Reason::DstIsNotYetSupported => {
format!("analyzing the transmutability of `{dst}` is not yet supported")
}
rustc_transmute::Reason::DstIsBitIncompatible => {
Reason::DstIsBitIncompatible => {
format!(
"at least one value of `{src}` isn't a bit-valid value of `{dst}`"
)
}
rustc_transmute::Reason::DstUninhabited => {
Reason::DstUninhabited => {
format!("`{dst}` is uninhabited")
}
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
Reason::DstMayHaveSafetyInvariants => {
format!("`{dst}` may carry safety invariants")
}
rustc_transmute::Reason::DstIsTooBig => {
Reason::DstIsTooBig => {
format!("the size of `{src}` is smaller than the size of `{dst}`")
}
rustc_transmute::Reason::DstRefIsTooBig {
src,
src_size,
dst,
dst_size,
} => {
Reason::DstRefIsTooBig { src, src_size, dst, dst_size } => {
format!(
"the size of `{src}` ({src_size} bytes) \
is smaller than that of `{dst}` ({dst_size} bytes)"
)
}
rustc_transmute::Reason::SrcSizeOverflow => {
Reason::SrcSizeOverflow => {
format!(
"values of the type `{src}` are too big for the target architecture"
)
}
rustc_transmute::Reason::DstSizeOverflow => {
Reason::DstSizeOverflow => {
format!(
"values of the type `{dst}` are too big for the target architecture"
)
}
rustc_transmute::Reason::DstHasStricterAlignment {
src_min_align,
dst_min_align,
} => {
Reason::DstHasStricterAlignment { src_min_align, dst_min_align } => {
format!(
"the minimum alignment of `{src}` ({src_min_align}) should be \
greater than that of `{dst}` ({dst_min_align})"
)
}
rustc_transmute::Reason::DstIsMoreUnique => {
Reason::DstIsMoreUnique => {
format!(
"`{src}` is a shared reference, but `{dst}` is a unique reference"
)
}
// Already reported by rustc
rustc_transmute::Reason::TypeError => {
Reason::TypeError => {
return GetSafeTransmuteErrorAndReason::Silent;
}
rustc_transmute::Reason::SrcLayoutUnknown => {
Reason::SrcLayoutUnknown => {
format!("`{src}` has an unknown layout")
}
rustc_transmute::Reason::DstLayoutUnknown => {
Reason::DstLayoutUnknown => {
format!("`{dst}` has an unknown layout")
}
};
Expand Down
22 changes: 9 additions & 13 deletions compiler/rustc_trait_selection/src/solve/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ use rustc_infer::traits::solve::{
};
use rustc_lint_defs::builtin::RECURSION_DEPTH_EXCEEDING_LIMIT;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{Certainty, MaybeInfo};
use rustc_middle::traits::solve::MaybeInfo;
use rustc_middle::ty::print::{FmtPrinter, Print};
use rustc_middle::ty::transmute::Answer;
use rustc_middle::ty::{
self, CanonicalizerState, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
self, CanonicalizerState, MayBeErased, Region, Ty, TyCtxt, TypeFlags, TypeFoldable,
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
};
use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar};
use rustc_span::{DUMMY_SP, Span};
use rustc_structures::Limit;
use rustc_transmute::TransmuteTypeEnv;
use thin_vec::{ThinVec, thin_vec};

use super::inspect::InferCtxtProofTreeExt;
Expand Down Expand Up @@ -491,20 +493,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
src: Ty<'tcx>,
dst: Ty<'tcx>,
assume: ty::Const<'tcx>,
) -> Result<Certainty, NoSolution> {
) -> Result<Answer<Region<'tcx>, Ty<'tcx>>, NoSolution> {
// Erase regions because we compute layouts in `rustc_transmute`,
// which will ICE for region vars.
let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src));

let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else {
return Err(NoSolution);
};

// FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) {
rustc_transmute::Answer::Yes => Ok(Certainty::Yes),
rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution),
}
rustc_transmute::Assume::from_const(self.tcx, assume)
.map(|assume| TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume))
.ok_or(NoSolution)
}

fn obtain_canonicalizer_state(&self) -> CanonicalizerState<Self::Interner> {
Expand Down
Loading
Loading