Auto merge of #139979 - matthiaskrgr:rollup-7wu5169, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #139774 (Fix replacing supertrait aliases in `ReplaceProjectionWith`)
 - #139850 (Hide unstable print kinds within emit_unknown_print_request_help in stable channel)
 - #139870 (add retries to remove and create dir all)
 - #139902 (do not emit `OpaqueCast` projections with `-Znext-solver`)
 - #139931 (bootstrap: enable zlib for LLVM for Windows GNU)
 - #139935 (Upgrade to `rustc-rayon-core` 0.5.1)
 - #139943 (rustdoc: Support inlined cross-crate re-exported trait aliases)
 - #139961 (Two `rustc_const_eval` cleanups)
 - #139962 (opt-dist: add a flag for running tests)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-04-17 16:33:06 +00:00
commit a15cce2690
45 changed files with 597 additions and 236 deletions

View file

@ -3199,14 +3199,12 @@ dependencies = [
[[package]]
name = "rustc-rayon-core"
version = "0.5.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67668daaf00e359c126f6dcb40d652d89b458a008c8afa727a42a2d20fca0b7f"
checksum = "2f42932dcd3bcbe484b38a3ccf79b7906fac41c02d408b5b1bac26da3416efdb"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]

View file

@ -34,7 +34,7 @@ use crate::check_consts::is_fn_or_trait_safe_to_expose_on_stable;
use crate::errors;
type QualifResults<'mir, 'tcx, Q> =
rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'tcx, Q>>;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ConstConditionsHold {

View file

@ -22,17 +22,17 @@ use super::{ConstCx, Qualif, qualifs};
/// qualified immediately after it is borrowed or its address escapes. The borrow must allow for
/// mutation, which includes shared borrows of places with interior mutability. The type of
/// borrowed place must contain the qualif.
struct TransferFunction<'a, 'mir, 'tcx, Q> {
ccx: &'a ConstCx<'mir, 'tcx>,
state: &'a mut State,
struct TransferFunction<'mir, 'tcx, Q> {
ccx: &'mir ConstCx<'mir, 'tcx>,
state: &'mir mut State,
_qualif: PhantomData<Q>,
}
impl<'a, 'mir, 'tcx, Q> TransferFunction<'a, 'mir, 'tcx, Q>
impl<'mir, 'tcx, Q> TransferFunction<'mir, 'tcx, Q>
where
Q: Qualif,
{
fn new(ccx: &'a ConstCx<'mir, 'tcx>, state: &'a mut State) -> Self {
fn new(ccx: &'mir ConstCx<'mir, 'tcx>, state: &'mir mut State) -> Self {
TransferFunction { ccx, state, _qualif: PhantomData }
}
@ -124,7 +124,7 @@ where
}
}
impl<'tcx, Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q>
impl<'tcx, Q> Visitor<'tcx> for TransferFunction<'_, 'tcx, Q>
where
Q: Qualif,
{
@ -228,20 +228,20 @@ where
}
/// The dataflow analysis used to propagate qualifs on arbitrary CFGs.
pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> {
ccx: &'a ConstCx<'mir, 'tcx>,
pub(super) struct FlowSensitiveAnalysis<'mir, 'tcx, Q> {
ccx: &'mir ConstCx<'mir, 'tcx>,
_qualif: PhantomData<Q>,
}
impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>
impl<'mir, 'tcx, Q> FlowSensitiveAnalysis<'mir, 'tcx, Q>
where
Q: Qualif,
{
pub(super) fn new(_: Q, ccx: &'a ConstCx<'mir, 'tcx>) -> Self {
pub(super) fn new(_: Q, ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
FlowSensitiveAnalysis { ccx, _qualif: PhantomData }
}
fn transfer_function(&self, state: &'a mut State) -> TransferFunction<'a, 'mir, 'tcx, Q> {
fn transfer_function(&self, state: &'mir mut State) -> TransferFunction<'mir, 'tcx, Q> {
TransferFunction::<Q>::new(self.ccx, state)
}
}
@ -313,7 +313,7 @@ impl JoinSemiLattice for State {
}
}
impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, 'tcx, Q>
where
Q: Qualif,
{

View file

@ -30,6 +30,7 @@ use super::{
AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok,
};
use crate::const_eval;
use crate::const_eval::DummyMachine;
use crate::errors::NestedStaticInThreadLocal;
pub trait CompileTimeMachine<'tcx, T> = Machine<
@ -323,14 +324,17 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>
interp_ok(())
}
impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> {
impl<'tcx> InterpCx<'tcx, DummyMachine> {
/// A helper function that allocates memory for the layout given and gives you access to mutate
/// it. Once your own mutation code is done, the backing `Allocation` is removed from the
/// current `Memory` and interned as read-only into the global memory.
pub fn intern_with_temp_alloc(
&mut self,
layout: TyAndLayout<'tcx>,
f: impl FnOnce(&mut InterpCx<'tcx, M>, &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, ()>,
f: impl FnOnce(
&mut InterpCx<'tcx, DummyMachine>,
&PlaceTy<'tcx, CtfeProvenance>,
) -> InterpResult<'tcx, ()>,
) -> InterpResult<'tcx, AllocId> {
// `allocate` picks a fresh AllocId that we will associate with its data below.
let dest = self.allocate(layout, MemoryKind::Stack)?;

View file

@ -1502,16 +1502,21 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
let mut projections = base_place.place.projections;
let node_ty = self.cx.typeck_results().node_type(node);
// Opaque types can't have field projections, but we can instead convert
// the current place in-place (heh) to the hidden type, and then apply all
// follow up projections on that.
if node_ty != place_ty
&& self
.cx
.try_structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), place_ty)
.is_impl_trait()
{
projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
if !self.cx.tcx().next_trait_solver_globally() {
// Opaque types can't have field projections, but we can instead convert
// the current place in-place (heh) to the hidden type, and then apply all
// follow up projections on that.
if node_ty != place_ty
&& self
.cx
.try_structurally_resolve_type(
self.cx.tcx().hir_span(base_place.hir_id),
place_ty,
)
.is_impl_trait()
{
projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
}
}
projections.push(Projection { kind, ty });
PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections)

View file

@ -40,6 +40,8 @@ pub enum ProjectionKind {
/// A conversion from an opaque type to its hidden type so we can
/// do further projections on it.
///
/// This is unused if `-Znext-solver` is enabled.
OpaqueCast,
}

View file

@ -1242,6 +1242,8 @@ pub enum ProjectionElem<V, T> {
/// Like an explicit cast from an opaque type to a concrete type, but without
/// requiring an intermediate variable.
///
/// This is unused with `-Znext-solver`.
OpaqueCast(T),
/// A transmute from an unsafe binder to the type that it wraps. This is a projection

View file

@ -101,18 +101,21 @@ impl<'tcx> MatchPairTree<'tcx> {
place_builder = resolved;
}
// Only add the OpaqueCast projection if the given place is an opaque type and the
// expected type from the pattern is not.
let may_need_cast = match place_builder.base() {
PlaceBase::Local(local) => {
let ty =
Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty;
ty != pattern.ty && ty.has_opaque_types()
if !cx.tcx.next_trait_solver_globally() {
// Only add the OpaqueCast projection if the given place is an opaque type and the
// expected type from the pattern is not.
let may_need_cast = match place_builder.base() {
PlaceBase::Local(local) => {
let ty =
Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
.ty;
ty != pattern.ty && ty.has_opaque_types()
}
_ => true,
};
if may_need_cast {
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
}
_ => true,
};
if may_need_cast {
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
}
let place = place_builder.try_to_place(cx);

View file

@ -39,20 +39,22 @@ impl<'tcx> MutVisitor<'tcx> for PostAnalysisNormalizeVisitor<'tcx> {
_context: PlaceContext,
_location: Location,
) {
// Performance optimization: don't reintern if there is no `OpaqueCast` to remove.
if place.projection.iter().all(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_))) {
return;
if !self.tcx.next_trait_solver_globally() {
// `OpaqueCast` projections are only needed if there are opaque types on which projections
// are performed. After the `PostAnalysisNormalize` pass, all opaque types are replaced with their
// hidden types, so we don't need these projections anymore.
//
// Performance optimization: don't reintern if there is no `OpaqueCast` to remove.
if place.projection.iter().any(|elem| matches!(elem, ProjectionElem::OpaqueCast(_))) {
place.projection = self.tcx.mk_place_elems(
&place
.projection
.into_iter()
.filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_)))
.collect::<Vec<_>>(),
);
};
}
// `OpaqueCast` projections are only needed if there are opaque types on which projections
// are performed. After the `PostAnalysisNormalize` pass, all opaque types are replaced with their
// hidden types, so we don't need these projections anymore.
place.projection = self.tcx.mk_place_elems(
&place
.projection
.into_iter()
.filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_)))
.collect::<Vec<_>>(),
);
self.super_place(place, _context, _location);
}

View file

@ -92,16 +92,20 @@ where
let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else {
panic!("expected object type in `probe_and_consider_object_bound_candidate`");
};
ecx.add_goals(
GoalSource::ImplWhereBound,
structural_traits::predicates_for_object_candidate(
ecx,
goal.param_env,
goal.predicate.trait_ref(cx),
bounds,
),
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
match structural_traits::predicates_for_object_candidate(
ecx,
goal.param_env,
goal.predicate.trait_ref(cx),
bounds,
) {
Ok(requirements) => {
ecx.add_goals(GoalSource::ImplWhereBound, requirements);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
Err(_) => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
})
}

View file

@ -5,9 +5,10 @@ use derive_where::derive_where;
use rustc_type_ir::data_structures::HashMap;
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::TraitSolverLangItem;
use rustc_type_ir::solve::inspect::ProbeKind;
use rustc_type_ir::{
self as ty, Interner, Movability, Mutability, TypeFoldable, TypeFolder, TypeSuperFoldable,
Upcast as _, elaborate,
self as ty, FallibleTypeFolder, Interner, Movability, Mutability, TypeFoldable,
TypeSuperFoldable, Upcast as _, elaborate,
};
use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
use tracing::instrument;
@ -822,22 +823,16 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
/// impl Baz for dyn Foo<Item = Ty> {}
/// ```
///
/// However, in order to make such impls well-formed, we need to do an
/// However, in order to make such impls non-cyclical, we need to do an
/// additional step of eagerly folding the associated types in the where
/// clauses of the impl. In this example, that means replacing
/// `<Self as Foo>::Bar` with `Ty` in the first impl.
///
// FIXME: This is only necessary as `<Self as Trait>::Assoc: ItemBound`
// bounds in impls are trivially proven using the item bound candidates.
// This is unsound in general and once that is fixed, we don't need to
// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9
// for more details.
pub(in crate::solve) fn predicates_for_object_candidate<D, I>(
ecx: &EvalCtxt<'_, D>,
ecx: &mut EvalCtxt<'_, D>,
param_env: I::ParamEnv,
trait_ref: ty::TraitRef<I>,
object_bounds: I::BoundExistentialPredicates,
) -> Vec<Goal<I, I::Predicate>>
) -> Result<Vec<Goal<I, I::Predicate>>, Ambiguous>
where
D: SolverDelegate<Interner = I>,
I: Interner,
@ -871,72 +866,130 @@ where
.extend(cx.item_bounds(associated_type_def_id).iter_instantiated(cx, trait_ref.args));
}
let mut replace_projection_with = HashMap::default();
let mut replace_projection_with: HashMap<_, Vec<_>> = HashMap::default();
for bound in object_bounds.iter() {
if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() {
// FIXME: We *probably* should replace this with a dummy placeholder,
// b/c don't want to replace literal instances of this dyn type that
// show up in the bounds, but just ones that come from substituting
// `Self` with the dyn type.
let proj = proj.with_self_ty(cx, trait_ref.self_ty());
let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj));
assert_eq!(
old_ty,
None,
"{:?} has two generic parameters: {:?} and {:?}",
proj.projection_term,
proj.term,
old_ty.unwrap()
);
replace_projection_with.entry(proj.def_id()).or_default().push(bound.rebind(proj));
}
}
let mut folder =
ReplaceProjectionWith { ecx, param_env, mapping: replace_projection_with, nested: vec![] };
let folded_requirements = requirements.fold_with(&mut folder);
let mut folder = ReplaceProjectionWith {
ecx,
param_env,
self_ty: trait_ref.self_ty(),
mapping: &replace_projection_with,
nested: vec![],
};
folder
let requirements = requirements.try_fold_with(&mut folder)?;
Ok(folder
.nested
.into_iter()
.chain(folded_requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause)))
.collect()
.chain(requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause)))
.collect())
}
struct ReplaceProjectionWith<'a, D: SolverDelegate<Interner = I>, I: Interner> {
ecx: &'a EvalCtxt<'a, D>,
struct ReplaceProjectionWith<'a, 'b, I: Interner, D: SolverDelegate<Interner = I>> {
ecx: &'a mut EvalCtxt<'b, D>,
param_env: I::ParamEnv,
mapping: HashMap<I::DefId, ty::Binder<I, ty::ProjectionPredicate<I>>>,
self_ty: I::Ty,
mapping: &'a HashMap<I::DefId, Vec<ty::Binder<I, ty::ProjectionPredicate<I>>>>,
nested: Vec<Goal<I, I::Predicate>>,
}
impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I>
for ReplaceProjectionWith<'_, D, I>
impl<D, I> ReplaceProjectionWith<'_, '_, I, D>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
fn projection_may_match(
&mut self,
source_projection: ty::Binder<I, ty::ProjectionPredicate<I>>,
target_projection: ty::AliasTerm<I>,
) -> bool {
source_projection.item_def_id() == target_projection.def_id
&& self
.ecx
.probe(|_| ProbeKind::ProjectionCompatibility)
.enter(|ecx| -> Result<_, NoSolution> {
let source_projection = ecx.instantiate_binder_with_infer(source_projection);
ecx.eq(self.param_env, source_projection.projection_term, target_projection)?;
ecx.try_evaluate_added_goals()
})
.is_ok()
}
/// Try to replace an alias with the term present in the projection bounds of the self type.
/// Returns `Ok<None>` if this alias is not eligible to be replaced, or bail with
/// `Err(Ambiguous)` if it's uncertain which projection bound to replace the term with due
/// to multiple bounds applying.
fn try_eagerly_replace_alias(
&mut self,
alias_term: ty::AliasTerm<I>,
) -> Result<Option<I::Term>, Ambiguous> {
if alias_term.self_ty() != self.self_ty {
return Ok(None);
}
let Some(replacements) = self.mapping.get(&alias_term.def_id) else {
return Ok(None);
};
// This is quite similar to the `projection_may_match` we use in unsizing,
// but here we want to unify a projection predicate against an alias term
// so we can replace it with the the projection predicate's term.
let mut matching_projections = replacements
.iter()
.filter(|source_projection| self.projection_may_match(**source_projection, alias_term));
let Some(replacement) = matching_projections.next() else {
// This shouldn't happen.
panic!("could not replace {alias_term:?} with term from from {:?}", self.self_ty);
};
// FIXME: This *may* have issues with duplicated projections.
if matching_projections.next().is_some() {
// If there's more than one projection that we can unify here, then we
// need to stall until inference constrains things so that there's only
// one choice.
return Err(Ambiguous);
}
let replacement = self.ecx.instantiate_binder_with_infer(*replacement);
self.nested.extend(
self.ecx
.eq_and_get_goals(self.param_env, alias_term, replacement.projection_term)
.expect("expected to be able to unify goal projection with dyn's projection"),
);
Ok(Some(replacement.term))
}
}
/// Marker for bailing with ambiguity.
pub(crate) struct Ambiguous;
impl<D, I> FallibleTypeFolder<I> for ReplaceProjectionWith<'_, '_, I, D>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
type Error = Ambiguous;
fn cx(&self) -> I {
self.ecx.cx()
}
fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
fn try_fold_ty(&mut self, ty: I::Ty) -> Result<I::Ty, Ambiguous> {
if let ty::Alias(ty::Projection, alias_ty) = ty.kind() {
if let Some(replacement) = self.mapping.get(&alias_ty.def_id) {
// We may have a case where our object type's projection bound is higher-ranked,
// but the where clauses we instantiated are not. We can solve this by instantiating
// the binder at the usage site.
let proj = self.ecx.instantiate_binder_with_infer(*replacement);
// FIXME: Technically this equate could be fallible...
self.nested.extend(
self.ecx
.eq_and_get_goals(
self.param_env,
alias_ty,
proj.projection_term.expect_ty(self.ecx.cx()),
)
.expect(
"expected to be able to unify goal projection with dyn's projection",
),
);
proj.term.expect_ty()
} else {
ty.super_fold_with(self)
if let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())? {
return Ok(term.expect_ty());
}
} else {
ty.super_fold_with(self)
}
ty.try_super_fold_with(self)
}
}

View file

@ -944,7 +944,7 @@ where
target_projection: ty::Binder<I, ty::ExistentialProjection<I>>| {
source_projection.item_def_id() == target_projection.item_def_id()
&& ecx
.probe(|_| ProbeKind::UpcastProjectionCompatibility)
.probe(|_| ProbeKind::ProjectionCompatibility)
.enter(|ecx| -> Result<_, NoSolution> {
ecx.enter_forall(target_projection, |ecx, target_projection| {
let source_projection =

View file

@ -2065,7 +2065,8 @@ fn collect_print_requests(
check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind));
*print_kind
} else {
emit_unknown_print_request_help(early_dcx, req)
let is_nightly = nightly_options::match_is_nightly_build(matches);
emit_unknown_print_request_help(early_dcx, req, is_nightly)
};
let out = out.unwrap_or(OutFileName::Stdout);
@ -2089,25 +2090,37 @@ fn check_print_request_stability(
unstable_opts: &UnstableOptions,
(print_name, print_kind): (&str, PrintKind),
) {
if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options {
early_dcx.early_fatal(format!(
"the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \
print option"
));
}
}
fn is_print_request_stable(print_kind: PrintKind) -> bool {
match print_kind {
PrintKind::AllTargetSpecsJson
| PrintKind::CheckCfg
| PrintKind::CrateRootLintLevels
| PrintKind::SupportedCrateTypes
| PrintKind::TargetSpecJson
if !unstable_opts.unstable_options =>
{
early_dcx.early_fatal(format!(
"the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \
print option"
));
}
_ => {}
| PrintKind::TargetSpecJson => false,
_ => true,
}
}
fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str) -> ! {
let prints = PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>();
fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! {
let prints = PRINT_KINDS
.iter()
.filter_map(|(name, kind)| {
// If we're not on nightly, we don't want to print unstable options
if !is_nightly && !is_print_request_stable(*kind) {
None
} else {
Some(format!("`{name}`"))
}
})
.collect::<Vec<_>>();
let prints = prints.join(", ");
let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`"));

View file

@ -292,7 +292,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
inspect::ProbeStep::NestedProbe(ref probe) => {
match probe.kind {
// These never assemble candidates for the goal we're trying to solve.
inspect::ProbeKind::UpcastProjectionCompatibility
inspect::ProbeKind::ProjectionCompatibility
| inspect::ProbeKind::ShadowedEnvProbing => continue,
inspect::ProbeKind::NormalizedSelfTyAssembly
@ -314,8 +314,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
}
match probe.kind {
inspect::ProbeKind::UpcastProjectionCompatibility
| inspect::ProbeKind::ShadowedEnvProbing => bug!(),
inspect::ProbeKind::ProjectionCompatibility
| inspect::ProbeKind::ShadowedEnvProbing => {
bug!()
}
inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {}

View file

@ -177,7 +177,7 @@ fn to_selection<'tcx>(
},
ProbeKind::NormalizedSelfTyAssembly
| ProbeKind::UnsizeAssembly
| ProbeKind::UpcastProjectionCompatibility
| ProbeKind::ProjectionCompatibility
| ProbeKind::OpaqueTypeStorageLookup { result: _ }
| ProbeKind::Root { result: _ }
| ProbeKind::ShadowedEnvProbing

View file

@ -118,10 +118,12 @@ pub enum ProbeKind<I: Interner> {
/// Used in the probe that wraps normalizing the non-self type for the unsize
/// trait, which is also structurally matched on.
UnsizeAssembly,
/// During upcasting from some source object to target object type, used to
/// do a probe to find out what projection type(s) may be used to prove that
/// the source type upholds all of the target type's object bounds.
UpcastProjectionCompatibility,
/// Used to do a probe to find out what projection type(s) match a given
/// alias bound or projection predicate. For trait upcasting, this is used
/// to prove that the source type upholds all of the target type's object
/// bounds. For object type bounds, this is used when eagerly replacing
/// supertrait aliases.
ProjectionCompatibility,
/// Looking for param-env candidates that satisfy the trait ref for a projection.
ShadowedEnvProbing,
/// Try to unify an opaque type with an existing key in the storage.

View file

@ -1,4 +1,4 @@
Change this file to make users of the `download-ci-llvm` configuration download
a new version of LLVM from CI, even if the LLVM submodule hasnt changed.
Last change is for: https://github.com/rust-lang/rust/pull/138784
Last change is for: https://github.com/rust-lang/rust/pull/139931

View file

@ -370,8 +370,8 @@ impl Step for Llvm {
cfg.define("LLVM_PROFDATA_FILE", path);
}
// Libraries for ELF section compression.
if !target.is_windows() {
// Libraries for ELF section compression and profraw files merging.
if !target.is_msvc() {
cfg.define("LLVM_ENABLE_ZLIB", "ON");
} else {
cfg.define("LLVM_ENABLE_ZLIB", "OFF");

View file

@ -22,21 +22,27 @@ where
/// A wrapper around [`std::fs::remove_dir_all`] that can also be used on *non-directory entries*,
/// including files and symbolic links.
///
/// - This will produce an error if the target path is not found.
/// - This will not produce an error if the target path is not found.
/// - Like [`std::fs::remove_dir_all`], this helper does not traverse symbolic links, will remove
/// symbolic link itself.
/// - This helper is **not** robust against races on the underlying filesystem, behavior is
/// unspecified if this helper is called concurrently.
/// - This helper is not robust against TOCTOU problems.
///
/// FIXME: this implementation is insufficiently robust to replace bootstrap's clean `rm_rf`
/// implementation:
///
/// - This implementation currently does not perform retries.
/// FIXME: Audit whether this implementation is robust enough to replace bootstrap's clean `rm_rf`.
#[track_caller]
pub fn recursive_remove<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref();
let metadata = fs::symlink_metadata(path)?;
// If the path doesn't exist, we treat it as a successful no-op.
// From the caller's perspective, the goal is simply "ensure this file/dir is gone" —
// if it's already not there, that's a success, not an error.
let metadata = match fs::symlink_metadata(path) {
Ok(m) => m,
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
Err(e) => return Err(e),
};
#[cfg(windows)]
let is_dir_like = |meta: &fs::Metadata| {
use std::os::windows::fs::FileTypeExt;
@ -45,11 +51,35 @@ pub fn recursive_remove<P: AsRef<Path>>(path: P) -> io::Result<()> {
#[cfg(not(windows))]
let is_dir_like = fs::Metadata::is_dir;
if is_dir_like(&metadata) {
fs::remove_dir_all(path)
} else {
try_remove_op_set_perms(fs::remove_file, path, metadata)
const MAX_RETRIES: usize = 5;
const RETRY_DELAY_MS: u64 = 100;
let try_remove = || {
if is_dir_like(&metadata) {
fs::remove_dir_all(path)
} else {
try_remove_op_set_perms(fs::remove_file, path, metadata.clone())
}
};
// Retry deletion a few times to handle transient filesystem errors.
// This is unusual for local file operations, but it's a mitigation
// against unlikely events where malware scanners may be holding a
// file beyond our control, to give the malware scanners some opportunity
// to release their hold.
for attempt in 0..MAX_RETRIES {
match try_remove() {
Ok(()) => return Ok(()),
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
Err(_) if attempt < MAX_RETRIES - 1 => {
std::thread::sleep(std::time::Duration::from_millis(RETRY_DELAY_MS));
continue;
}
Err(e) => return Err(e),
}
}
Ok(())
}
fn try_remove_op_set_perms<'p, Op>(mut op: Op, path: &'p Path, metadata: Metadata) -> io::Result<()>
@ -67,3 +97,9 @@ where
Err(e) => Err(e),
}
}
pub fn remove_and_create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref();
recursive_remove(path)?;
fs::create_dir_all(path)
}

View file

@ -14,7 +14,7 @@ mod recursive_remove_tests {
let tmpdir = env::temp_dir();
let path = tmpdir.join("__INTERNAL_BOOTSTRAP_nonexistent_path");
assert!(fs::symlink_metadata(&path).is_err_and(|e| e.kind() == io::ErrorKind::NotFound));
assert!(recursive_remove(&path).is_err_and(|e| e.kind() == io::ErrorKind::NotFound));
assert!(recursive_remove(&path).is_ok());
}
#[test]

View file

@ -67,9 +67,13 @@ pub(crate) fn try_inline(
record_extern_fqn(cx, did, ItemType::Trait);
cx.with_param_env(did, |cx| {
build_impls(cx, did, attrs_without_docs, &mut ret);
clean::TraitItem(Box::new(build_external_trait(cx, did)))
clean::TraitItem(Box::new(build_trait(cx, did)))
})
}
Res::Def(DefKind::TraitAlias, did) => {
record_extern_fqn(cx, did, ItemType::TraitAlias);
cx.with_param_env(did, |cx| clean::TraitAliasItem(build_trait_alias(cx, did)))
}
Res::Def(DefKind::Fn, did) => {
record_extern_fqn(cx, did, ItemType::Function);
cx.with_param_env(did, |cx| {
@ -251,7 +255,7 @@ pub(crate) fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemT
}
}
pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait {
pub(crate) fn build_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait {
let trait_items = cx
.tcx
.associated_items(did)
@ -263,11 +267,18 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
let predicates = cx.tcx.predicates_of(did);
let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
let generics = filter_non_trait_generics(did, generics);
let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
let (generics, supertrait_bounds) = separate_self_bounds(generics);
clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds }
}
pub(crate) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box<clean::Function> {
fn build_trait_alias(cx: &mut DocContext<'_>, did: DefId) -> clean::TraitAlias {
let predicates = cx.tcx.predicates_of(did);
let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
let (generics, bounds) = separate_self_bounds(generics);
clean::TraitAlias { generics, bounds }
}
pub(super) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box<clean::Function> {
let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
// The generics need to be cleaned before the signature.
let mut generics =
@ -788,12 +799,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
g
}
/// Supertrait bounds for a trait are also listed in the generics coming from
/// the metadata for a crate, so we want to separate those out and create a new
/// list of explicit supertrait bounds to render nicely.
fn separate_supertrait_bounds(
mut g: clean::Generics,
) -> (clean::Generics, Vec<clean::GenericBound>) {
fn separate_self_bounds(mut g: clean::Generics) -> (clean::Generics, Vec<clean::GenericBound>) {
let mut ty_bounds = Vec::new();
g.where_predicates.retain(|pred| match *pred {
clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref bounds, .. } => {
@ -806,22 +812,17 @@ fn separate_supertrait_bounds(
}
pub(crate) fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) {
if did.is_local() {
if did.is_local()
|| cx.external_traits.contains_key(&did)
|| cx.active_extern_traits.contains(&did)
{
return;
}
{
if cx.external_traits.contains_key(&did) || cx.active_extern_traits.contains(&did) {
return;
}
}
{
cx.active_extern_traits.insert(did);
}
cx.active_extern_traits.insert(did);
debug!("record_extern_trait: {did:?}");
let trait_ = build_external_trait(cx, did);
let trait_ = build_trait(cx, did);
cx.external_traits.insert(did, trait_);
cx.active_extern_traits.remove(&did);

View file

@ -27,7 +27,7 @@ use rustc_span::source_map;
use rustc_span::symbol::sym;
use tracing::{debug, info};
use crate::clean::inline::build_external_trait;
use crate::clean::inline::build_trait;
use crate::clean::{self, ItemId};
use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
use crate::formats::cache::Cache;
@ -385,7 +385,7 @@ pub(crate) fn run_global_ctxt(
//
// Note that in case of `#![no_core]`, the trait is not available.
if let Some(sized_trait_did) = ctxt.tcx.lang_items().sized_trait() {
let sized_trait = build_external_trait(&mut ctxt, sized_trait_did);
let sized_trait = build_trait(&mut ctxt, sized_trait_did);
ctxt.external_traits.insert(sized_trait_did, sized_trait);
}

View file

@ -1232,12 +1232,13 @@ fn item_trait_alias(
wrap_item(w, |w| {
write!(
w,
"{attrs}trait {name}{generics}{where_b} = {bounds};",
"{attrs}trait {name}{generics} = {bounds}{where_clause};",
attrs = render_attributes_in_pre(it, "", cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(),
bounds = bounds(&t.bounds, true, cx),
where_clause =
print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(),
)
})?;

View file

@ -9,6 +9,7 @@ use std::process::{Child, Command, ExitStatus, Output, Stdio};
use std::sync::Arc;
use std::{env, iter, str};
use build_helper::fs::remove_and_create_dir_all;
use camino::{Utf8Path, Utf8PathBuf};
use colored::Colorize;
use regex::{Captures, Regex};
@ -207,12 +208,6 @@ pub fn compute_stamp_hash(config: &Config) -> String {
format!("{:x}", hash.finish())
}
fn remove_and_create_dir_all(path: &Utf8Path) {
let path = path.as_std_path();
let _ = fs::remove_dir_all(path);
fs::create_dir_all(path).unwrap();
}
#[derive(Copy, Clone, Debug)]
struct TestCx<'test> {
config: &'test Config,
@ -523,7 +518,9 @@ impl<'test> TestCx<'test> {
let mut rustc = Command::new(&self.config.rustc_path);
let out_dir = self.output_base_name().with_extension("pretty-out");
remove_and_create_dir_all(&out_dir);
remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
});
let target = if self.props.force_host { &*self.config.host } else { &*self.config.target };
@ -1098,13 +1095,19 @@ impl<'test> TestCx<'test> {
let aux_dir = self.aux_output_dir_name();
if !self.props.aux.builds.is_empty() {
remove_and_create_dir_all(&aux_dir);
remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| {
panic!("failed to remove and recreate output directory `{aux_dir}`: {e}")
});
}
if !self.props.aux.bins.is_empty() {
let aux_bin_dir = self.aux_bin_output_dir_name();
remove_and_create_dir_all(&aux_dir);
remove_and_create_dir_all(&aux_bin_dir);
remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| {
panic!("failed to remove and recreate output directory `{aux_dir}`: {e}")
});
remove_and_create_dir_all(&aux_bin_dir).unwrap_or_else(|e| {
panic!("failed to remove and recreate output directory `{aux_bin_dir}`: {e}")
});
}
aux_dir
@ -1509,7 +1512,9 @@ impl<'test> TestCx<'test> {
let set_mir_dump_dir = |rustc: &mut Command| {
let mir_dump_dir = self.get_mir_dump_dir();
remove_and_create_dir_all(&mir_dump_dir);
remove_and_create_dir_all(&mir_dump_dir).unwrap_or_else(|e| {
panic!("failed to remove and recreate output directory `{mir_dump_dir}`: {e}")
});
let mut dir_opt = "-Zdump-mir-dir=".to_string();
dir_opt.push_str(mir_dump_dir.as_str());
debug!("dir_opt: {:?}", dir_opt);
@ -1969,7 +1974,9 @@ impl<'test> TestCx<'test> {
let suffix =
self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly");
let compare_dir = output_base_dir(self.config, self.testpaths, Some(&suffix));
remove_and_create_dir_all(&compare_dir);
remove_and_create_dir_all(&compare_dir).unwrap_or_else(|e| {
panic!("failed to remove and recreate output directory `{compare_dir}`: {e}")
});
// We need to create a new struct for the lifetimes on `config` to work.
let new_rustdoc = TestCx {

View file

@ -7,7 +7,9 @@ impl TestCx<'_> {
assert!(self.revision.is_none(), "revisions not relevant here");
let out_dir = self.output_base_dir();
remove_and_create_dir_all(&out_dir);
remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
});
let proc_res = self.document(&out_dir, &self.testpaths);
if !proc_res.status.success() {

View file

@ -9,7 +9,9 @@ impl TestCx<'_> {
assert!(self.revision.is_none(), "revisions not relevant here");
let out_dir = self.output_base_dir();
remove_and_create_dir_all(&out_dir);
remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
});
let proc_res = self.document(&out_dir, &self.testpaths);
if !proc_res.status.success() {

View file

@ -25,6 +25,7 @@ pub struct Environment {
prebuilt_rustc_perf: Option<Utf8PathBuf>,
use_bolt: bool,
shared_llvm: bool,
run_tests: bool,
}
impl Environment {
@ -101,6 +102,10 @@ impl Environment {
pub fn benchmark_cargo_config(&self) -> &[String] {
&self.benchmark_cargo_config
}
pub fn run_tests(&self) -> bool {
self.run_tests
}
}
/// What is the extension of binary executables on this platform?

View file

@ -94,6 +94,10 @@ enum EnvironmentCmd {
/// Arguments passed to `rustc-perf --cargo-config <value>` when running benchmarks.
#[arg(long)]
benchmark_cargo_config: Vec<String>,
/// Perform tests after final build if it's not a try build
#[arg(long)]
run_tests: bool,
},
/// Perform an optimized build on Linux CI, from inside Docker.
LinuxCi {
@ -125,6 +129,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
skipped_tests,
benchmark_cargo_config,
shared,
run_tests,
} => {
let env = EnvironmentBuilder::default()
.host_tuple(target_triple)
@ -138,6 +143,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
.use_bolt(use_bolt)
.skipped_tests(skipped_tests)
.benchmark_cargo_config(benchmark_cargo_config)
.run_tests(run_tests)
.build()?;
(env, shared.build_args)
@ -160,6 +166,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
// FIXME: Enable bolt for aarch64 once it's fixed upstream. Broken as of December 2024.
.use_bolt(!is_aarch64)
.skipped_tests(vec![])
.run_tests(true)
.build()?;
(env, shared.build_args)
@ -179,6 +186,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
.shared_llvm(false)
.use_bolt(false)
.skipped_tests(vec![])
.run_tests(true)
.build()?;
(env, shared.build_args)
@ -344,7 +352,7 @@ fn execute_pipeline(
// possible regressions.
// The tests are not executed for try builds, which can be in various broken states, so we don't
// want to gatekeep them with tests.
if !is_try_build() {
if !is_try_build() && env.run_tests() {
timer.section("Run tests", |_| run_tests(env))?;
}

View file

@ -260,7 +260,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"constant_time_eq",
"cpufeatures",
"crc32fast",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-utils",
@ -295,7 +294,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"gimli",
"gsgdt",
"hashbrown",
"hermit-abi",
"icu_list",
"icu_list_data",
"icu_locid",
@ -329,7 +327,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"miniz_oxide",
"nix",
"nu-ansi-term",
"num_cpus",
"object",
"odht",
"once_cell",

View file

@ -0,0 +1,7 @@
@@ -1,5 +1,5 @@
error: unknown print request: `xxx`
|
- = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models`
+ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information

View file

@ -0,0 +1,33 @@
//! Check that unstable print requests are omitted from help if compiler is in stable channel.
//!
//! Issue: <https://github.com/rust-lang/rust/issues/138698>
use run_make_support::{diff, rustc, similar};
fn main() {
let stable_invalid_print_request_help = rustc()
.env("RUSTC_BOOTSTRAP", "-1")
.cfg("force_stable")
.print("xxx")
.run_fail()
.stderr_utf8();
assert!(!stable_invalid_print_request_help.contains("all-target-specs-json"));
diff()
.expected_file("stable-invalid-print-request-help.err")
.actual_text("stable_invalid_print_request_help", &stable_invalid_print_request_help)
.run();
let unstable_invalid_print_request_help = rustc().print("xxx").run_fail().stderr_utf8();
assert!(unstable_invalid_print_request_help.contains("all-target-specs-json"));
diff()
.expected_file("unstable-invalid-print-request-help.err")
.actual_text("unstable_invalid_print_request_help", &unstable_invalid_print_request_help)
.run();
let help_diff = similar::TextDiff::from_lines(
&stable_invalid_print_request_help,
&unstable_invalid_print_request_help,
)
.unified_diff()
.to_string();
diff().expected_file("help-diff.diff").actual_text("help_diff", help_diff).run();
}

View file

@ -0,0 +1,5 @@
error: unknown print request: `xxx`
|
= help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information

View file

@ -0,0 +1,5 @@
error: unknown print request: `xxx`
|
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
= help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information

View file

@ -0,0 +1,13 @@
#![feature(trait_alias)]
pub trait ExtAlias0 = Copy + Iterator<Item = u8>;
pub trait ExtAlias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>;
pub trait ExtAlias2<T> = where T: From<String>, String: Into<T>;
pub trait ExtAlias3 = Sized;
pub trait ExtAlias4 = where Self: Sized;
pub trait ExtAlias5 = ;

View file

@ -1,3 +0,0 @@
#![feature(trait_alias)]
pub trait SomeAlias = std::fmt::Debug + std::marker::Copy;

View file

@ -1,10 +0,0 @@
//@ aux-build:trait-alias-mention.rs
//@ build-aux-docs
#![crate_name = "foo"]
extern crate trait_alias_mention;
//@ has foo/fn.mention_alias_in_bounds.html '//a[@href="../trait_alias_mention/traitalias.SomeAlias.html"]' 'SomeAlias'
pub fn mention_alias_in_bounds<T: trait_alias_mention::SomeAlias>() {
}

View file

@ -0,0 +1,82 @@
// Basic testing for trait aliases.
#![feature(trait_alias)]
#![crate_name = "it"]
// Check the "local case" (HIR cleaning) //
//@ has it/all.html '//a[@href="traitalias.Alias0.html"]' 'Alias0'
//@ has it/index.html '//h2[@id="trait-aliases"]' 'Trait Aliases'
//@ has it/index.html '//a[@class="traitalias"]' 'Alias0'
//@ has it/traitalias.Alias0.html
//@ has - '//*[@class="rust item-decl"]//code' 'trait Alias0 = Copy + Iterator<Item = u8>;'
pub trait Alias0 = Copy + Iterator<Item = u8>;
//@ has it/traitalias.Alias1.html
//@ has - '//pre[@class="rust item-decl"]' \
// "trait Alias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>;"
pub trait Alias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>;
//@ has it/traitalias.Alias2.html
//@ has - '//pre[@class="rust item-decl"]' \
// 'trait Alias2<T> = where T: From<String>, String: Into<T>;'
pub trait Alias2<T> = where T: From<String>, String: Into<T>;
//@ has it/traitalias.Alias3.html
//@ has - '//pre[@class="rust item-decl"]' 'trait Alias3 = ;'
pub trait Alias3 =;
//@ has it/traitalias.Alias4.html
//@ has - '//pre[@class="rust item-decl"]' 'trait Alias4 = ;'
pub trait Alias4 = where;
//@ has it/fn.usage0.html
//@ has - '//pre[@class="rust item-decl"]' "pub fn usage0(_: impl Alias0)"
//@ has - '//a[@href="traitalias.Alias0.html"]' 'Alias0'
pub fn usage0(_: impl Alias0) {}
// FIXME: One can only "disambiguate" intra-doc links to trait aliases with `type@` but not with
// `trait@` (fails to resolve) or `traitalias@` (doesn't exist). We should make at least one of
// the latter two work, right?
//@ has it/link0/index.html
//@ has - '//a/@href' 'traitalias.Alias0.html'
//@ has - '//a/@href' 'traitalias.Alias1.html'
/// [Alias0], [type@Alias1]
pub mod link0 {}
// Check the "extern case" (middle cleaning) //
//@ aux-build: ext-trait-aliases.rs
extern crate ext_trait_aliases as ext;
//@ has it/traitalias.ExtAlias0.html
//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias0 = Copy + Iterator<Item = u8>;'
pub use ext::ExtAlias0;
//@ has it/traitalias.ExtAlias1.html
//@ has - '//pre[@class="rust item-decl"]' \
// "trait ExtAlias1<'a, T, const N: usize> = From<[&'a T; N]> where T: 'a + Clone;"
pub use ext::ExtAlias1;
//@ has it/traitalias.ExtAlias2.html
//@ has - '//pre[@class="rust item-decl"]' \
// 'trait ExtAlias2<T> = where T: From<String>, String: Into<T>;'
pub use ext::ExtAlias2;
//@ has it/traitalias.ExtAlias3.html
//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias3 = Sized;'
pub use ext::ExtAlias3;
// NOTE: Middle cleaning can't discern `= Sized` and `= where Self: Sized` and that's okay.
//@ has it/traitalias.ExtAlias4.html
//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias4 = Sized;'
pub use ext::ExtAlias4;
//@ has it/traitalias.ExtAlias5.html
//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias5 = ;'
pub use ext::ExtAlias5;
//@ has it/fn.usage1.html
//@ has - '//pre[@class="rust item-decl"]' "pub fn usage1(_: impl ExtAlias0)"
//@ has - '//a[@href="traitalias.ExtAlias0.html"]' 'ExtAlias0'
pub fn usage1(_: impl ExtAlias0) {}

View file

@ -1,26 +0,0 @@
#![feature(trait_alias)]
#![crate_name = "foo"]
use std::fmt::Debug;
//@ has foo/all.html '//a[@href="traitalias.CopyAlias.html"]' 'CopyAlias'
//@ has foo/all.html '//a[@href="traitalias.Alias2.html"]' 'Alias2'
//@ has foo/all.html '//a[@href="traitalias.Foo.html"]' 'Foo'
//@ has foo/index.html '//h2[@id="trait-aliases"]' 'Trait Aliases'
//@ has foo/index.html '//a[@class="traitalias"]' 'CopyAlias'
//@ has foo/index.html '//a[@class="traitalias"]' 'Alias2'
//@ has foo/index.html '//a[@class="traitalias"]' 'Foo'
//@ has foo/traitalias.CopyAlias.html
//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait CopyAlias = Copy;'
pub trait CopyAlias = Copy;
//@ has foo/traitalias.Alias2.html
//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait Alias2 = Copy + Debug;'
pub trait Alias2 = Copy + Debug;
//@ has foo/traitalias.Foo.html
//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait Foo<T> = Into<T> + Debug;'
pub trait Foo<T> = Into<T> + Debug;
//@ has foo/fn.bar.html '//a[@href="traitalias.Alias2.html"]' 'Alias2'
pub fn bar<T>() where T: Alias2 {}

View file

@ -0,0 +1,22 @@
//@ compile-flags: -Znext-solver
//@ check-pass
// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/171>.
// Tests that we don't try to replace `<V as Super>::Output` when replacing projections in the
// required bounds for `dyn Trait`, b/c `V` is not relevant to the dyn type, which we were
// previously encountering b/c we were walking into the existential projection bounds of the dyn
// type itself.
pub trait Trait: Super {}
pub trait Super {
type Output;
}
fn bound<T: Trait + ?Sized>() {}
fn visit_simd_operator<V: Super + ?Sized>() {
bound::<dyn Trait<Output = <V as Super>::Output>>();
}
fn main() {}

View file

@ -0,0 +1,25 @@
//@ compile-flags: -Znext-solver
//@ check-pass
// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/171>.
// Tests that we don't try to replace `<T as Other>::Assoc` when replacing projections in the
// required bounds for `dyn Foo`, b/c `T` is not relevant to the dyn type, which we were
// encountering when walking through the elaborated supertraits of `dyn Foo`.
trait Other<X> {}
trait Foo<T: Foo<T>>: Other<<T as Foo<T>>::Assoc> {
type Assoc;
}
impl<T> Foo<T> for T {
type Assoc = ();
}
impl<T: ?Sized> Other<()> for T {}
fn is_foo<T: Foo<()> + ?Sized>() {}
fn main() {
is_foo::<dyn Foo<(), Assoc = ()>>();
}

View file

@ -0,0 +1,32 @@
//@ compile-flags: -Znext-solver
//@ check-pass
// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/171>.
// Exercises a case where structural equality is insufficient when replacing projections in a dyn's
// bounds. In this case, the bound will contain `<Self as Super<<i32 as Mirror>:Assoc>::Assoc`, but
// the existential projections from the dyn will have `<Self as Super<i32>>::Assoc` because as an
// optimization we eagerly normalize aliases in goals.
trait Other<T> {}
impl<T> Other<T> for T {}
trait Super<T> {
type Assoc;
}
trait Mirror {
type Assoc;
}
impl<T> Mirror for T {
type Assoc = T;
}
trait Foo<A, B>: Super<<A as Mirror>::Assoc, Assoc = A> {
type FooAssoc: Other<<Self as Super<<A as Mirror>::Assoc>>::Assoc>;
}
fn is_foo<F: Foo<T, U> + ?Sized, T, U>() {}
fn main() {
is_foo::<dyn Foo<i32, u32, FooAssoc = i32>, _, _>();
}

View file

@ -0,0 +1,24 @@
//@ compile-flags: -Znext-solver
//@ check-pass
// Exercises the ambiguity that comes from replacing the associated types within the bounds
// that are required for a `impl Trait for dyn Trait` built-in object impl to hold.
trait Sup<T> {
type Assoc;
}
trait Foo<A, B>: Sup<A, Assoc = A> + Sup<B, Assoc = B> {
type Other: Bar<<Self as Sup<A>>::Assoc>;
}
trait Bar<T> {}
impl Bar<i32> for () {}
fn foo<A, B>(x: &(impl Foo<A, B> + ?Sized)) {}
fn main() {
let x: &dyn Foo<_, _, Other = ()> = todo!();
foo(x);
let y: &dyn Foo<i32, u32, Other = ()> = x;
}

View file

@ -1,6 +1,5 @@
//@ compile-flags: --crate-type=lib
//@ edition: 2021
//@ rustc-env:RUST_BACKTRACE=0
//@ check-pass
// tracked in https://github.com/rust-lang/rust/issues/96572

View file

@ -1,3 +1,5 @@
//@ revisions: current next
//@ [next] compile-flags: -Znext-solver
//@ build-pass
//@ edition: 2021

View file

@ -1,3 +1,5 @@
//@ revisions: current next
//@ [next] compile-flags: -Znext-solver
//@ check-pass
#![feature(type_alias_impl_trait)]