Auto merge of #149533 - adwinwhite:skip-trait-goal-if-possible, r= · rust-lang/rust@405e127 (original) (raw)

`@@ -17,7 +17,6 @@ use rustc_type_ir::{

`

17

17

`};

`

18

18

`use tracing::{debug, instrument};

`

19

19

``

20

``

`-

use super::trait_goals::TraitGoalProvenVia;

`

21

20

`use super::{has_only_region_constraints, inspect};

`

22

21

`use crate::delegate::SolverDelegate;

`

23

22

`use crate::solve::inspect::ProbeKind;

`

`@@ -363,13 +362,15 @@ pub(super) enum AssembleCandidatesFrom {

`

363

362

`` /// user-written and built-in impls. We only expect ParamEnv and AliasBound

``

364

363

`/// candidates to be assembled.

`

365

364

`EnvAndBounds,

`

``

365

`+

Impl,

`

366

366

`}

`

367

367

``

368

368

`impl AssembleCandidatesFrom {

`

369

369

`fn should_assemble_impl_candidates(&self) -> bool {

`

370

370

`match self {

`

371

371

`AssembleCandidatesFrom::All => true,

`

372

372

`AssembleCandidatesFrom::EnvAndBounds => false,

`

``

373

`+

AssembleCandidatesFrom::Impl => true,

`

373

374

`}

`

374

375

`}

`

375

376

`}

`

`@@ -426,11 +427,14 @@ where

`

426

427

`return (candidates, failed_candidate_info);

`

427

428

`}

`

428

429

``

429

``

`-

self.assemble_alias_bound_candidates(goal, &mut candidates);

`

430

``

`-

self.assemble_param_env_candidates(goal, &mut candidates, &mut failed_candidate_info);

`

431

``

-

432

430

`match assemble_from {

`

433

431

`AssembleCandidatesFrom::All => {

`

``

432

`+

self.assemble_alias_bound_candidates(goal, &mut candidates);

`

``

433

`+

self.assemble_param_env_candidates(

`

``

434

`+

goal,

`

``

435

`+

&mut candidates,

`

``

436

`+

&mut failed_candidate_info,

`

``

437

`+

);

`

434

438

`self.assemble_builtin_impl_candidates(goal, &mut candidates);

`

435

439

`// For performance we only assemble impls if there are no candidates

`

436

440

`// which would shadow them. This is necessary to avoid hangs in rayon,

`

`@@ -457,6 +461,12 @@ where

`

457

461

`}

`

458

462

`}

`

459

463

`AssembleCandidatesFrom::EnvAndBounds => {

`

``

464

`+

self.assemble_alias_bound_candidates(goal, &mut candidates);

`

``

465

`+

self.assemble_param_env_candidates(

`

``

466

`+

goal,

`

``

467

`+

&mut candidates,

`

``

468

`+

&mut failed_candidate_info,

`

``

469

`+

);

`

460

470

`// This is somewhat inconsistent and may make #57893 slightly easier to exploit.

`

461

471

`// However, it matches the behavior of the old solver. See

`

462

472

`` // tests/ui/traits/next-solver/normalization-shadowing/use_object_if_empty_env.rs.

``

`@@ -466,6 +476,10 @@ where

`

466

476

`self.assemble_object_bound_candidates(goal, &mut candidates);

`

467

477

`}

`

468

478

`}

`

``

479

`+

AssembleCandidatesFrom::Impl => {

`

``

480

`+

self.assemble_builtin_impl_candidates(goal, &mut candidates);

`

``

481

`+

self.assemble_impl_candidates(goal, &mut candidates);

`

``

482

`+

}

`

469

483

`}

`

470

484

``

471

485

`(candidates, failed_candidate_info)

`

`@@ -1097,112 +1111,6 @@ where

`

1097

1111

`}

`

1098

1112

`}

`

1099

1113

``

1100

``

`-

/// Assemble and merge candidates for goals which are related to an underlying trait

`

1101

``

`-

/// goal. Right now, this is normalizes-to and host effect goals.

`

1102

``

`-

///

`

1103

``

`-

/// We sadly can't simply take all possible candidates for normalization goals

`

1104

``

`-

/// and check whether they result in the same constraints. We want to make sure

`

1105

``

`-

/// that trying to normalize an alias doesn't result in constraints which aren't

`

1106

``

`-

/// otherwise required.

`

1107

``

`-

///

`

1108

``

`-

/// Most notably, when proving a trait goal by via a where-bound, we should not

`

1109

``

`-

/// normalize via impls which have stricter region constraints than the where-bound:

`

1110

``

`-

///

`

1111

``


/// ```rust

1112

``

`-

/// trait Trait<'a> {

`

1113

``

`-

/// type Assoc;

`

1114

``

`-

/// }

`

1115

``

`-

///

`

1116

``

`-

/// impl<'a, T: 'a> Trait<'a> for T {

`

1117

``

`-

/// type Assoc = u32;

`

1118

``

`-

/// }

`

1119

``

`-

///

`

1120

``

`-

/// fn with_bound<'a, T: Trait<'a>>(_value: T::Assoc) {}

`

1121

``


/// ```

1122

``

`-

///

`

1123

``

`` -

/// The where-bound of with_bound doesn't specify the associated type, so we would

``

1124

``

`` -

/// only be able to normalize <T as Trait<'a>>::Assoc by using the impl. This impl

``

1125

``

`` -

/// adds a T: 'a bound however, which would result in a region error. Given that the

``

1126

``

`` -

/// user explicitly wrote that T: Trait<'a> holds, this is undesirable and we instead

``

1127

``

`-

/// treat the alias as rigid.

`

1128

``

`-

///

`

1129

``

`-

/// See trait-system-refactor-initiative#124 for more details.

`

1130

``

`-

#[instrument(level = "debug", skip_all, fields(proven_via, goal), ret)]

`

1131

``

`-

pub(super) fn assemble_and_merge_candidates<G: GoalKind>(

`

1132

``

`-

&mut self,

`

1133

``

`-

proven_via: Option,

`

1134

``

`-

goal: Goal<I, G>,

`

1135

``

`-

inject_forced_ambiguity_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> Option<QueryResult>,

`

1136

``

`-

inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult,

`

1137

``

`-

) -> QueryResult {

`

1138

``

`-

let Some(proven_via) = proven_via else {

`

1139

``

`-

// We don't care about overflow. If proving the trait goal overflowed, then

`

1140

``

`-

// it's enough to report an overflow error for that, we don't also have to

`

1141

``

`-

// overflow during normalization.

`

1142

``

`-

//

`

1143

``

`` -

// We use forced_ambiguity here over make_ambiguous_response_no_constraints

``

1144

``

`-

// because the former will also record a built-in candidate in the inspector.

`

1145

``

`-

return self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result);

`

1146

``

`-

};

`

1147

``

-

1148

``

`-

match proven_via {

`

1149

``

`-

TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {

`

1150

``

`-

// Even when a trait bound has been proven using a where-bound, we

`

1151

``

`-

// still need to consider alias-bounds for normalization, see

`

1152

``

`` -

// tests/ui/next-solver/alias-bound-shadowed-by-env.rs.

``

1153

``

`-

let (mut candidates, _) = self

`

1154

``

`-

.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);

`

1155

``

`-

debug!(?candidates);

`

1156

``

-

1157

``

`-

// If the trait goal has been proven by using the environment, we want to treat

`

1158

``

`-

// aliases as rigid if there are no applicable projection bounds in the environment.

`

1159

``

`-

if candidates.is_empty() {

`

1160

``

`-

return inject_normalize_to_rigid_candidate(self);

`

1161

``

`-

}

`

1162

``

-

1163

``

`-

// If we're normalizing an GAT, we bail if using a where-bound would constrain

`

1164

``

`-

// its generic arguments.

`

1165

``

`-

if let Some(result) = inject_forced_ambiguity_candidate(self) {

`

1166

``

`-

return result;

`

1167

``

`-

}

`

1168

``

-

1169

``

`-

// We still need to prefer where-bounds over alias-bounds however.

`

1170

``

`` -

// See tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs.

``

1171

``

`-

if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {

`

1172

``

`-

candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));

`

1173

``

`-

}

`

1174

``

-

1175

``

`-

if let Some((response, _)) = self.try_merge_candidates(&candidates) {

`

1176

``

`-

Ok(response)

`

1177

``

`-

} else {

`

1178

``

`-

self.flounder(&candidates)

`

1179

``

`-

}

`

1180

``

`-

}

`

1181

``

`-

TraitGoalProvenVia::Misc => {

`

1182

``

`-

let (mut candidates, _) =

`

1183

``

`-

self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);

`

1184

``

-

1185

``

`-

// Prefer "orphaned" param-env normalization predicates, which are used

`

1186

``

`-

// (for example, and ideally only) when proving item bounds for an impl.

`

1187

``

`-

if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {

`

1188

``

`-

candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));

`

1189

``

`-

}

`

1190

``

-

1191

``

`-

// We drop specialized impls to allow normalization via a final impl here. In case

`

1192

``

`-

// the specializing impl has different inference constraints from the specialized

`

1193

``

`-

// impl, proving the trait goal is already ambiguous, so we never get here. This

`

1194

``

`-

// means we can just ignore inference constraints and don't have to special-case

`

1195

``

`` -

// constraining the normalized-to term.

``

1196

``

`-

self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);

`

1197

``

`-

if let Some((response, _)) = self.try_merge_candidates(&candidates) {

`

1198

``

`-

Ok(response)

`

1199

``

`-

} else {

`

1200

``

`-

self.flounder(&candidates)

`

1201

``

`-

}

`

1202

``

`-

}

`

1203

``

`-

}

`

1204

``

`-

}

`

1205

``

-

1206

1114

`/// Compute whether a param-env assumption is global or non-global after normalizing it.

`

1207

1115

`///

`

1208

1116

`/// This is necessary because, for example, given:

`