Auto merge of #139845 - Zalathar:rollup-u5u5y1v, r=Zalathar
Rollup of 17 pull requests Successful merges: - #138374 (Enable contracts for const functions) - #138380 (ci: add runners for vanilla LLVM 20) - #138393 (Allow const patterns of matches to contain pattern types) - #139517 (std: sys: process: uefi: Use NULL stdin by default) - #139554 (std: add Output::exit_ok) - #139660 (compiletest: Add an experimental new executor to replace libtest) - #139669 (Overhaul `AssocItem`) - #139671 (Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file}) - #139750 (std/thread: Use default stack size from menuconfig for NuttX) - #139772 (Remove `hir::Map`) - #139785 (Let CStrings be either 1 or 2 byte aligned.) - #139789 (do not unnecessarily leak auto traits in item bounds) - #139791 (drop global where-bounds before merging candidates) - #139798 (normalize: prefer `ParamEnv` over `AliasBound` candidates) - #139822 (Fix: Map EOPNOTSUPP to ErrorKind::Unsupported on Unix) - #139833 (Fix some HIR pretty-printing problems) - #139836 (Basic tests of MPMC receiver cloning) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
@@ -792,37 +792,46 @@ where
|
||||
};
|
||||
|
||||
match proven_via {
|
||||
// Even when a trait bound has been proven using a where-bound, we
|
||||
// still need to consider alias-bounds for normalization, see
|
||||
// tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
|
||||
//
|
||||
// FIXME(const_trait_impl): should this behavior also be used by
|
||||
// constness checking. Doing so is *at least theoretically* breaking,
|
||||
// see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
|
||||
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
|
||||
let mut candidates_from_env_and_bounds: Vec<_> = candidates
|
||||
.iter()
|
||||
.filter(|c| {
|
||||
matches!(
|
||||
c.source,
|
||||
CandidateSource::AliasBound | CandidateSource::ParamEnv(_)
|
||||
)
|
||||
})
|
||||
.map(|c| c.result)
|
||||
.collect();
|
||||
let mut considered_candidates = Vec::new();
|
||||
considered_candidates.extend(
|
||||
candidates
|
||||
.iter()
|
||||
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
|
||||
.map(|c| c.result),
|
||||
);
|
||||
|
||||
// Even when a trait bound has been proven using a where-bound, we
|
||||
// still need to consider alias-bounds for normalization, see
|
||||
// tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
|
||||
//
|
||||
// We still need to prefer where-bounds over alias-bounds however.
|
||||
// See tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs.
|
||||
//
|
||||
// FIXME(const_trait_impl): should this behavior also be used by
|
||||
// constness checking. Doing so is *at least theoretically* breaking,
|
||||
// see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
|
||||
if considered_candidates.is_empty() {
|
||||
considered_candidates.extend(
|
||||
candidates
|
||||
.iter()
|
||||
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
|
||||
.map(|c| c.result),
|
||||
);
|
||||
}
|
||||
|
||||
// If the trait goal has been proven by using the environment, we want to treat
|
||||
// aliases as rigid if there are no applicable projection bounds in the environment.
|
||||
if candidates_from_env_and_bounds.is_empty() {
|
||||
if considered_candidates.is_empty() {
|
||||
if let Ok(response) = inject_normalize_to_rigid_candidate(self) {
|
||||
candidates_from_env_and_bounds.push(response);
|
||||
considered_candidates.push(response);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(response) = self.try_merge_responses(&candidates_from_env_and_bounds) {
|
||||
if let Some(response) = self.try_merge_responses(&considered_candidates) {
|
||||
Ok(response)
|
||||
} else {
|
||||
self.flounder(&candidates_from_env_and_bounds)
|
||||
self.flounder(&considered_candidates)
|
||||
}
|
||||
}
|
||||
TraitGoalProvenVia::Misc => {
|
||||
|
||||
@@ -164,6 +164,7 @@ where
|
||||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let cx = ecx.cx();
|
||||
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
@@ -174,20 +175,37 @@ where
|
||||
|
||||
// Only consider auto impls of unsafe traits when there are no unsafe
|
||||
// fields.
|
||||
if ecx.cx().trait_is_unsafe(goal.predicate.def_id())
|
||||
if cx.trait_is_unsafe(goal.predicate.def_id())
|
||||
&& goal.predicate.self_ty().has_unsafe_fields()
|
||||
{
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// We only look into opaque types during analysis for opaque types
|
||||
// outside of their defining scope. Doing so for opaques in the
|
||||
// defining scope may require calling `typeck` on the same item we're
|
||||
// currently type checking, which will result in a fatal cycle that
|
||||
// ideally we want to avoid, since we can make progress on this goal
|
||||
// via an alias bound or a locally-inferred hidden type instead.
|
||||
// We leak the implemented auto traits of opaques outside of their defining scope.
|
||||
// This depends on `typeck` of the defining scope of that opaque, which may result in
|
||||
// fatal query cycles.
|
||||
//
|
||||
// We only get to this point if we're outside of the defining scope as we'd otherwise
|
||||
// be able to normalize the opaque type. We may also cycle in case `typeck` of a defining
|
||||
// scope relies on the current context, e.g. either because it also leaks auto trait
|
||||
// bounds of opaques defined in the current context or by evaluating the current item.
|
||||
//
|
||||
// To avoid this we don't try to leak auto trait bounds if they can also be proven via
|
||||
// item bounds of the opaque. These bounds are always applicable as auto traits must not
|
||||
// have any generic parameters. They would also get preferred over the impl candidate
|
||||
// when merging candidates anyways.
|
||||
//
|
||||
// See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs.
|
||||
if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
|
||||
debug_assert!(ecx.opaque_type_is_rigid(opaque_ty.def_id));
|
||||
for item_bound in cx.item_self_bounds(opaque_ty.def_id).skip_binder() {
|
||||
if item_bound
|
||||
.as_trait_clause()
|
||||
.is_some_and(|b| b.def_id() == goal.predicate.def_id())
|
||||
{
|
||||
return Err(NoSolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
||||
@@ -1238,10 +1256,11 @@ where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
#[instrument(level = "debug", skip(self, goal), ret)]
|
||||
pub(super) fn merge_trait_candidates(
|
||||
&mut self,
|
||||
goal: Goal<I, TraitPredicate<I>>,
|
||||
candidates: Vec<Candidate<I>>,
|
||||
mut candidates: Vec<Candidate<I>>,
|
||||
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
|
||||
if let TypingMode::Coherence = self.typing_mode() {
|
||||
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
|
||||
@@ -1323,13 +1342,16 @@ where
|
||||
|
||||
// If there are *only* global where bounds, then make sure to return that this
|
||||
// is still reported as being proven-via the param-env so that rigid projections
|
||||
// operate correctly.
|
||||
// operate correctly. Otherwise, drop all global where-bounds before merging the
|
||||
// remaining candidates.
|
||||
let proven_via =
|
||||
if candidates.iter().all(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
|
||||
TraitGoalProvenVia::ParamEnv
|
||||
} else {
|
||||
candidates.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(_)));
|
||||
TraitGoalProvenVia::Misc
|
||||
};
|
||||
|
||||
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
|
||||
if let Some(response) = self.try_merge_responses(&all_candidates) {
|
||||
Ok((response, Some(proven_via)))
|
||||
|
||||
Reference in New Issue
Block a user