Skip to content

Commit 34e7e82

Browse files
committed
Sema: Fix non-determinism with ConstraintSystem::ConstraintRestrictions
We iterate over the DenseMap and simplify both types appearing in the key. If multiple keys simplify to the same type, later keys overwrite earlier keys, so the outcome depends on hash table order. Fix this by introducing an arbitrary tie break: we prefer the highest-numbered restriction. Fixes rdar://146780049.
1 parent 52b415e commit 34e7e82

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

lib/Sema/CSSolver.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,15 @@ Solution ConstraintSystem::finalize() {
124124

125125
CanType first = simplifyType(types.first)->getCanonicalType();
126126
CanType second = simplifyType(types.second)->getCanonicalType();
127+
128+
// Pick the restriction with the highest value to avoid depending on
129+
// iteration order.
130+
auto found = solution.ConstraintRestrictions.find({first, second});
131+
if (found != solution.ConstraintRestrictions.end() &&
132+
(unsigned) restriction <= (unsigned) found->second) {
133+
continue;
134+
}
135+
127136
solution.ConstraintRestrictions[{first, second}] = restriction;
128137
}
129138
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
2+
3+
// rdar://146780049
4+
5+
class Image {}
6+
7+
class MyV {
8+
var image: Image = Image()
9+
}
10+
11+
func takesPtr(_: UnsafePointer<Image>?, _: UnsafePointer<Image>?, _: UnsafePointer<Image>?) {
12+
}
13+
14+
func test(buffers: [MyV]) {
15+
withUnsafePointer(to: buffers[0].image) { ptrA in
16+
withUnsafePointer(to: buffers[1].image) { ptrB in
17+
withUnsafePointer(to: buffers[2].image) { ptrC in
18+
_ = takesPtr(ptrA, ptrB, ptrC)
19+
}
20+
}
21+
}
22+
}
23+
24+
// The other valid conversion here that should not be considered because it has a worse
25+
// score is pointer-to-pointer instead of value-to-optional. The generated SIL looks
26+
// quite different in that case, involving calls to intrinsics. Make sure we pick the
27+
// value-to-optional conversion, because it generates much simpler SIL:
28+
29+
// CHECK-LABEL: sil private [ossa] @$s28ambiguous_pointer_conversion4test7buffersySayAA3MyVCG_tFySPyAA5ImageCGXEfU_yAIXEfU_yAIXEfU_ : $@convention(thin) @substituted <τ_0_0, τ_0_1, τ_0_2> (UnsafePointer<τ_0_0>, UnsafePointer<Image>, UnsafePointer<Image>) -> (@out τ_0_2, @error_indirect τ_0_1) for <Image, Never, ()> {
30+
// CHECK: bb0(%0 : $*(), %1 : $*Never, %2 : $UnsafePointer<Image>, %3 : @closureCapture $UnsafePointer<Image>, %4 : @closureCapture $UnsafePointer<Image>):
31+
// CHECK: [[X:%.*]] = enum $Optional<UnsafePointer<Image>>, #Optional.some!enumelt, %3
32+
// CHECK: [[Y:%.*]] = enum $Optional<UnsafePointer<Image>>, #Optional.some!enumelt, %4
33+
// CHECK: [[Z:%.*]] = enum $Optional<UnsafePointer<Image>>, #Optional.some!enumelt, %2
34+
// CHECK: [[FN:%.*]] = function_ref @$s28ambiguous_pointer_conversion8takesPtryySPyAA5ImageCGSg_A2FtF : $@convention(thin) (Optional<UnsafePointer<Image>>, Optional<UnsafePointer<Image>>, Optional<UnsafePointer<Image>>) -> ()
35+
// CHECK: apply %11([[X]], [[Y]], [[Z]]) : $@convention(thin) (Optional<UnsafePointer<Image>>, Optional<UnsafePointer<Image>>, Optional<UnsafePointer<Image>>) -> ()
36+
// CHECK: return
37+

0 commit comments

Comments
 (0)