Use ExtractIf in fulfillment loop · rust-lang/rust@eb04ad7 (original) (raw)

1

1

`use std:📑:PhantomData;

`

2

``

`-

use std::mem;

`

3

2

`use std::ops::ControlFlow;

`

4

3

``

5

4

`use rustc_data_structures::thinvec::ExtractIf;

`

`@@ -75,6 +74,13 @@ impl<'tcx> ObligationStorage<'tcx> {

`

75

74

`self.pending.push((obligation, stalled_on));

`

76

75

`}

`

77

76

``

``

77

`+

fn register_overflowed(

`

``

78

`+

&mut self,

`

``

79

`+

overflowed: impl IntoIterator<Item = PredicateObligation<'tcx>>,

`

``

80

`+

) {

`

``

81

`+

self.overflowed.extend(overflowed);

`

``

82

`+

}

`

``

83

+

78

84

`fn has_pending_obligations(&self) -> bool {

`

79

85

` !self.pending.is_empty() || !self.overflowed.is_empty()

`

80

86

`}

`

`@@ -88,35 +94,10 @@ impl<'tcx> ObligationStorage<'tcx> {

`

88

94

``

89

95

`fn drain_pending(

`

90

96

`&mut self,

`

91

``

`-

cond: impl Fn(&PredicateObligation<'tcx>) -> bool,

`

92

``

`-

) -> PendingObligations<'tcx> {

`

93

``

`-

let (unstalled, pending) =

`

94

``

`-

mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o));

`

95

``

`-

self.pending = pending;

`

96

``

`-

unstalled

`

97

``

`-

}

`

98

``

-

99

``

`-

fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {

`

100

``

`-

infcx.probe(|_| {

`

101

``

`-

// IMPORTANT: we must not use solve any inference variables in the obligations

`

102

``

`-

// as this is all happening inside of a probe. We use a probe to make sure

`

103

``

`-

// we get all obligations involved in the overflow. We pretty much check: if

`

104

``

`` -

// we were to do another step of select_where_possible, which goals would

``

105

``

`-

// change.

`

106

``

`-

// FIXME: https://github.com/Gankra/thin-vec/pull/66 is merged, this can be removed.

`

107

``

`-

self.overflowed.extend(

`

108

``

`-

ExtractIf::new(&mut self.pending, |(o, stalled_on)| {

`

109

``

`-

let goal = o.as_goal();

`

110

``

`-

let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(

`

111

``

`-

goal,

`

112

``

`-

o.cause.span,

`

113

``

`-

stalled_on.take(),

`

114

``

`-

);

`

115

``

`-

matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))

`

116

``

`-

})

`

117

``

`-

.map(|(o, _)| o),

`

118

``

`-

);

`

119

``

`-

})

`

``

97

`+

cond: impl Fn(&PredicateObligation<'tcx>, Option<&GoalStalledOn<TyCtxt<'tcx>>>) -> bool,

`

``

98

`+

) -> impl Iterator<Item = (PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>

`

``

99

`+

{

`

``

100

`+

ExtractIf::new(&mut self.pending, move |(o, stalled)| cond(o, stalled.as_ref()))

`

120

101

`}

`

121

102

`}

`

122

103

``

`@@ -133,21 +114,6 @@ impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {

`

133

114

`_errors: PhantomData,

`

134

115

`}

`

135

116

`}

`

136

``

-

137

``

`-

fn inspect_evaluated_obligation(

`

138

``

`-

&self,

`

139

``

`-

infcx: &InferCtxt<'tcx>,

`

140

``

`-

obligation: &PredicateObligation<'tcx>,

`

141

``

`-

result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,

`

142

``

`-

) {

`

143

``

`-

if let Some(inspector) = infcx.obligation_inspector.get() {

`

144

``

`-

let result = match result {

`

145

``

`-

Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),

`

146

``

`-

Err(NoSolution) => Err(NoSolution),

`

147

``

`-

};

`

148

``

`-

(inspector)(infcx, &obligation, result);

`

149

``

`-

}

`

150

``

`-

}

`

151

117

`}

`

152

118

``

153

119

`impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>

`

`@@ -181,32 +147,42 @@ where

`

181

147

``

182

148

`fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec {

`

183

149

`assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());

`

``

150

`+

let delegate = <&SolverDelegate<'tcx>>::from(infcx);

`

184

151

`let mut errors = Vec::new();

`

185

152

`loop {

`

186

153

`let mut any_changed = false;

`

187

``

`-

for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) {

`

``

154

`+

let mut overflowed = vec![];

`

``

155

`+

let mut pending = vec![];

`

``

156

+

``

157

`+

for (mut obligation, stalled_on) in self.obligations.drain_pending(|_, stalled_on| {

`

``

158

`+

stalled_on.is_none_or(|s| !delegate.is_still_stalled(s))

`

``

159

`+

}) {

`

188

160

`if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {

`

189

``

`-

self.obligations.on_fulfillment_overflow(infcx);

`

190

``

`-

// Only return true errors that we have accumulated while processing.

`

191

``

`-

return errors;

`

``

161

`+

overflowed.push(obligation);

`

``

162

`+

continue;

`

192

163

`}

`

193

164

``

194

165

`let goal = obligation.as_goal();

`

195

``

`-

let delegate = <&SolverDelegate<'tcx>>::from(infcx);

`

196

166

`if let Some(certainty) =

`

197

167

` delegate.compute_goal_fast_path(goal, obligation.cause.span)

`

198

168

`{

`

199

169

`match certainty {

`

200

170

`Certainty::Yes => {}

`

201

``

`-

Certainty::Maybe(_) => {

`

202

``

`-

self.obligations.register(obligation, None);

`

203

``

`-

}

`

``

171

`+

Certainty::Maybe(_) => pending.push((obligation, None)),

`

204

172

`}

`

205

173

`continue;

`

206

174

`}

`

207

175

``

208

176

`let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);

`

209

``

`-

self.inspect_evaluated_obligation(infcx, &obligation, &result);

`

``

177

+

``

178

`+

if let Some(inspector) = infcx.obligation_inspector.get() {

`

``

179

`+

let result = match result {

`

``

180

`+

Ok(GoalEvaluation { certainty, .. }) => Ok(certainty),

`

``

181

`+

Err(NoSolution) => Err(NoSolution),

`

``

182

`+

};

`

``

183

`+

(inspector)(infcx, &obligation, result);

`

``

184

`+

}

`

``

185

+

210

186

`let GoalEvaluation { certainty, has_changed, stalled_on } = match result {

`

211

187

`Ok(result) => result,

`

212

188

`Err(NoSolution) => {

`

`@@ -231,10 +207,19 @@ where

`

231

207

``

232

208

`match certainty {

`

233

209

`Certainty::Yes => {}

`

234

``

`-

Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),

`

``

210

`+

Certainty::Maybe(_) => pending.push((obligation, stalled_on)),

`

235

211

`}

`

236

212

`}

`

237

213

``

``

214

`+

if !overflowed.is_empty() {

`

``

215

`+

self.obligations.register_overflowed(overflowed);

`

``

216

`+

return errors;

`

``

217

`+

}

`

``

218

+

``

219

`+

for (obligation, stalled_on) in pending {

`

``

220

`+

self.obligations.register(obligation, stalled_on);

`

``

221

`+

}

`

``

222

+

238

223

`if !any_changed {

`

239

224

`break;

`

240

225

`}

`

`@@ -270,7 +255,7 @@ where

`

270

255

`}

`

271

256

``

272

257

`self.obligations

`

273

``

`-

.drain_pending(|obl| {

`

``

258

`+

.drain_pending(|obl, _| {

`

274

259

` infcx.probe(|_| {

`

275

260

` infcx

`

276

261

`.visit_proof_tree(

`