@@ -208,6 +208,10 @@ func ==(_ lhs: BoundVersion, _ rhs: BoundVersion) -> Bool {
208
208
}
209
209
210
210
/// A container for constraints for a set of packages.
211
+ ///
212
+ /// This data structure is only designed to represent satisfiable constraint
213
+ /// sets, it cannot represent sets including containers which have an empty
214
+ /// constraint.
211
215
//
212
216
// FIXME: Maybe each package should just return this, instead of a list of
213
217
// `PackageContainerConstraint`s. That won't work if we decide this should
@@ -229,7 +233,10 @@ struct PackageContainerConstraintSet<C: PackageContainer>: Collection {
229
233
}
230
234
231
235
/// Create an constraint set from known values.
236
+ ///
237
+ /// The initial constraints should never be unsatisfiable.
232
238
init ( _ constraints: [ Identifier : VersionSetSpecifier ] ) {
239
+ assert ( constraints. values. filter ( { $0 == . empty } ) . isEmpty)
233
240
self . constraints = constraints
234
241
}
235
242
@@ -243,41 +250,45 @@ struct PackageContainerConstraintSet<C: PackageContainer>: Collection {
243
250
return constraints [ identifier] ?? . any
244
251
}
245
252
246
- /// Merge the given version requirement for the container `identifier`.
253
+ /// Create a constraint set by merging the `versionRequirement` for container `identifier`.
247
254
///
248
- /// - Returns: False if the merger has made the set unsatisfiable; i.e. true
249
- /// when the resulting set is satisfiable, if it was already so.
250
- private mutating func merge ( versionRequirement: VersionSetSpecifier , for identifier: Identifier ) -> Bool {
251
- let intersection : VersionSetSpecifier
252
- if let existing = constraints [ identifier ] {
253
- intersection = existing . intersection ( versionRequirement)
254
- } else {
255
- intersection = versionRequirement
255
+ /// - Returns: The new set, or nil the resulting set is unsatisfiable.
256
+ private func merging (
257
+ versionRequirement: VersionSetSpecifier , for identifier: Identifier
258
+ ) -> PackageContainerConstraintSet < C > ?
259
+ {
260
+ let intersection = self [ identifier ] . intersection ( versionRequirement)
261
+ if intersection == . empty {
262
+ return nil
256
263
}
257
- constraints [ identifier] = intersection
258
- return intersection != . empty
264
+ var result = self
265
+ result. constraints [ identifier] = intersection
266
+ return result
259
267
}
260
268
261
- /// Merge the given `constraint`.
269
+ /// Create a constraint set by merging `constraint`.
262
270
///
263
- /// - Returns: False if the merger has made the set unsatisfiable; i.e. true
264
- /// when the resulting set is satisfiable, if it was already so.
265
- mutating func merge( _ constraint: PackageContainerConstraint < Identifier > ) -> Bool {
266
- return merge ( versionRequirement: constraint. versionRequirement, for: constraint. identifier)
271
+ /// - Returns: The new set, or nil the resulting set is unsatisfiable.
272
+ func merging( _ constraint: PackageContainerConstraint < Identifier > ) -> PackageContainerConstraintSet < C > ? {
273
+ return merging ( versionRequirement: constraint. versionRequirement, for: constraint. identifier)
267
274
}
268
275
269
- /// Merge the given constraint set.
276
+ /// Create a new constraint set by merging the given constraint set.
270
277
///
271
278
/// - Returns: False if the merger has made the set unsatisfiable; i.e. true
272
279
/// when the resulting set is satisfiable, if it was already so.
273
- mutating func merge( _ constraints: PackageContainerConstraintSet < Container > ) -> Bool {
274
- var satisfiable = true
280
+ func merging(
281
+ _ constraints: PackageContainerConstraintSet < Container >
282
+ ) -> PackageContainerConstraintSet < C > ?
283
+ {
284
+ var result = self
275
285
for (key, versionRequirement) in constraints {
276
- if !merge ( versionRequirement: versionRequirement, for: key) {
277
- satisfiable = false
286
+ guard let merged = result . merging ( versionRequirement: versionRequirement, for: key) else {
287
+ return nil
278
288
}
289
+ result = merged
279
290
}
280
- return satisfiable
291
+ return result
281
292
}
282
293
283
294
// MARK: Collection Conformance
@@ -343,41 +354,36 @@ struct VersionAssignmentSet<C: PackageContainer>: Sequence {
343
354
}
344
355
}
345
356
346
- /// Merge in the bindings from the given `assignment`.
357
+ /// Create a new assignment set by merging in the bindings from `assignment`.
347
358
///
348
- /// - Returns: False if the merge cannot be made (the assignments contain
349
- /// incompatible versions).
350
- mutating func merge ( _ assignment: VersionAssignmentSet < Container > ) -> Bool {
359
+ /// - Returns: The new assignment, or nil if the merge cannot be made (the
360
+ /// assignments contain incompatible versions).
361
+ func merging ( _ assignment: VersionAssignmentSet < Container > ) -> VersionAssignmentSet < Container > ? {
351
362
// In order to protect the assignment set, we first have to test whether
352
363
// the merged constraint sets are satisfiable.
353
364
//
354
- // FIXME: Move to non-mutating methods with results, in order to have a
355
- // nice consistent API with `PackageContainerConstraintSet.merge`.
356
- //
357
365
// FIXME: This is very inefficient; we should decide whether it is right
358
366
// to handle it here or force the main resolver loop to handle the
359
367
// discovery of this property.
360
- var mergedConstraints = constraints
361
- guard mergedConstraints. merge ( assignment. constraints) else {
362
- return false
368
+ guard let _ = constraints. merging ( assignment. constraints) else {
369
+ return nil
363
370
}
364
371
365
372
// The induced constraints are satisfiable, so we *can* union the
366
373
// assignments without breaking our internal invariant on
367
374
// satisfiability.
375
+ var result = self
368
376
for (container, binding) in assignment {
369
- if let existing = self [ container] {
377
+ if let existing = result [ container] {
370
378
if existing != binding {
371
- // NOTE: We are returning here with the data structure
372
- // partially updated, which feels wrong. See FIXME above.
373
- return false
379
+ return nil
374
380
}
375
381
} else {
376
- self [ container] = binding
382
+ result [ container] = binding
377
383
}
378
384
}
379
385
380
- return true
386
+ return result
381
387
}
382
388
383
389
/// The combined version constraints induced by the assignment.
@@ -407,8 +413,10 @@ struct VersionAssignmentSet<C: PackageContainer>: Sequence {
407
413
// FIXME: Error handling, except that we probably shouldn't have
408
414
// needed to refetch the dependencies at this point.
409
415
for constraint in try ! container. getDependencies ( at: version) {
410
- let satisfiable = result. merge ( constraint)
411
- assert ( satisfiable)
416
+ guard let merged = result. merging ( constraint) else {
417
+ preconditionFailure ( " unsatisfiable constraint set " )
418
+ }
419
+ result = merged
412
420
}
413
421
}
414
422
}
@@ -661,9 +669,10 @@ public class DependencyResolver<
661
669
// FIXME: We should have a test for this, probably by adding some kind
662
670
// of statistics on the number of backtracks.
663
671
for constraint in constraints {
664
- if ! allConstraints. merge ( constraint) {
672
+ guard let merged = allConstraints. merging ( constraint) else {
665
673
return nil
666
674
}
675
+ allConstraints = merged
667
676
}
668
677
669
678
for constraint in constraints {
@@ -685,12 +694,9 @@ public class DependencyResolver<
685
694
throw DependencyResolverError . unimplemented
686
695
}
687
696
688
- // We found a valid assignment, attempt to merge it with the current solution.
689
- //
690
- // FIXME: It is rather important, subtle, and confusing that this
691
- // `merge` doesn't mutate the assignment but the one on the
692
- // constraint set does. We should probably make them consistent.
693
- guard assignment. merge ( subtreeAssignment) else {
697
+ // We found a valid subtree assignment, attempt to merge it with the
698
+ // current solution.
699
+ guard let newAssignment = assignment. merging ( subtreeAssignment) else {
694
700
// The assignment couldn't be merged with the current
695
701
// assignment, or the constraint sets couldn't be merged.
696
702
//
@@ -701,13 +707,16 @@ public class DependencyResolver<
701
707
throw DependencyResolverError . unimplemented
702
708
}
703
709
704
- // Merge the working constraint set.
710
+ // Update the working assignment and constraint set.
705
711
//
706
712
// This should always be feasible, because all prior constraints
707
713
// were part of the input constraint request (see comment around
708
714
// initial `merge` outside the loop).
709
- let mergable = allConstraints. merge ( subtreeAssignment. constraints)
710
- precondition ( mergable)
715
+ assignment = newAssignment
716
+ guard let merged = allConstraints. merging ( subtreeAssignment. constraints) else {
717
+ preconditionFailure ( " unsatisfiable constraints while merging subtree " )
718
+ }
719
+ allConstraints = merged
711
720
}
712
721
713
722
return assignment
0 commit comments