Here's some code:
var arr1 = [1, 2, 3, 4]
var arr2 = [1, 2, 3, 4]
if arr1 == arr2 {
println("Equal")
} else {
println("Not Equal")
}
// console output: Equal
let slice1 = arr1[0..4]
let slice2 = arr2[0..4]
if slice1 == slice2 {
println("Equal")
} else {
println("Not Equal")
}
// console output: Equal
It's straightforward, but code following:
if arr1[0..4] == arr2[0..4] {
println("Equal")
} else {
println("Not Equal")
}
// console output: Not Equal
Why the result is "Not Equal" rather than "Equal"?
Can I use == operator to compare the value type contents of two arrays or two slices? If not, why I could get the excepted results of the first two if
statement?
I believe this is a compiler bug that did not call the correct ==
overload function to compare Slice
under some conditions.
this is the minimum example I came out to produce the problem
let s1 : Slice<Int> = Slice(count: 1, repeatedValue:1)
let s2 : Slice<Int> = Slice(count: 1, repeatedValue:1)
var b1 = s1[0..1] == s2 // false
var b2 = s1 == s2 // true
In order to see what happened, I put it as a unit test in a Xcode project and check the disassembly code
Start with the correct code:
func testSlice2() {
b2 = s1 == s2
}
Xcode give me this
test2Tests`test2Tests.VariableTests.testSlice2 (test2Tests.VariableTests)() -> () at test2Tests.swift:38:
0x1000ec120: pushq %rbp
0x1000ec121: movq %rsp, %rbp
0x1000ec124: subq $0x80, %rsp
0x1000ec12b: movq %rdi, -0x8(%rbp)
0x1000ec12f: movq %rdi, -0x10(%rbp)
0x1000ec133: callq 0x1000efd86 ; symbol stub for: objc_retain
0x1000ec138: movq -0x10(%rbp), %rdi
0x1000ec13c: movq %rax, -0x18(%rbp)
0x1000ec140: callq 0x1000efd8c ; symbol stub for: object_getClass
0x1000ec145: movq -0x10(%rbp), %rdi
0x1000ec149: callq *0x50(%rax)
0x1000ec14c: movq -0x10(%rbp), %rdi
0x1000ec150: movq %rax, -0x20(%rbp)
0x1000ec154: movq %rdx, -0x28(%rbp)
0x1000ec158: movq %rcx, -0x30(%rbp)
0x1000ec15c: callq 0x1000efd86 ; symbol stub for: objc_retain
0x1000ec161: movq -0x10(%rbp), %rcx
0x1000ec165: movq %rcx, %rdi
0x1000ec168: movq %rax, -0x38(%rbp)
0x1000ec16c: callq 0x1000efd8c ; symbol stub for: object_getClass
0x1000ec171: movq 0x4ed8(%rip), %rcx ; (void *)0x00000001047bd8b0: direct type metadata for Swift.Int
0x1000ec178: addq $0x8, %rcx
0x1000ec17f: movq 0x4ef2(%rip), %rdx ; (void *)0x00000001047b6fc8: protocol witness table for Swift.Int : Swift.Equatable
0x1000ec186: movq -0x10(%rbp), %rdi
0x1000ec18a: movq %rdx, -0x40(%rbp)
0x1000ec18e: movq %rcx, -0x48(%rbp)
0x1000ec192: callq *0x58(%rax)
0x1000ec195: movq -0x20(%rbp), %rdi
0x1000ec199: movq -0x28(%rbp), %rsi
0x1000ec19d: movq -0x30(%rbp), %r8
0x1000ec1a1: movq %rdx, -0x50(%rbp)
0x1000ec1a5: movq %r8, %rdx
0x1000ec1a8: movq %rcx, -0x58(%rbp)
0x1000ec1ac: movq %rax, %rcx
0x1000ec1af: movq -0x50(%rbp), %r8
0x1000ec1b3: movq -0x58(%rbp), %r9
0x1000ec1b7: movq -0x48(%rbp), %rax
0x1000ec1bb: movq %rax, (%rsp)
0x1000ec1bf: movq -0x40(%rbp), %r10
0x1000ec1c3: movq %r10, 0x8(%rsp)
0x1000ec1c8: callq 0x1000efc3c ; symbol stub for: Swift.== @infix <A : Swift.Equatable>(Swift.Slice<A>, Swift.Slice<A>) -> Swift.Bool
0x1000ec1cd: movq -0x10(%rbp), %rdi
0x1000ec1d1: movb %al, -0x59(%rbp)
0x1000ec1d4: callq 0x1000efd86 ; symbol stub for: objc_retain
0x1000ec1d9: movb -0x59(%rbp), %r11b
0x1000ec1dd: movzbl %r11b, %edi
0x1000ec1e1: movq %rax, -0x68(%rbp)
0x1000ec1e5: callq 0x1000efdb0 ; symbol stub for: ObjectiveC._convertBoolToObjCBool (Swift.Bool) -> ObjectiveC.ObjCBool
0x1000ec1ea: movq 0x552f(%rip), %rsi ; "setB2:"
0x1000ec1f1: movq -0x10(%rbp), %rcx
0x1000ec1f5: movq %rcx, %rdi
0x1000ec1f8: movzbl %al, %edx
0x1000ec1fb: callq 0x1000efd6e ; symbol stub for: objc_msgSend
0x1000ec200: movq -0x10(%rbp), %rdi
0x1000ec204: callq 0x1000efd80 ; symbol stub for: objc_release
0x1000ec209: movq -0x10(%rbp), %rdi
0x1000ec20d: callq 0x1000efd80 ; symbol stub for: objc_release
0x1000ec212: addq $0x80, %rsp
0x1000ec219: popq %rbp
0x1000ec21a: retq
It is not hard to spot this line
0x1000ec1c8: callq 0x1000efc3c ; symbol stub for: Swift.== @infix <A : Swift.Equatable>(Swift.Slice<A>, Swift.Slice<A>) -> Swift.Bool
overloaded operator ==
is called to compare the value
Now for buggy code
func testSlice1() {
b1 = s1[0..1] == s2
}
Xcode give me this
test2Tests`test2Tests.VariableTests.testSlice1 (test2Tests.VariableTests)() -> () at test2Tests.swift:34:
0x1000ebec0: pushq %rbp
0x1000ebec1: movq %rsp, %rbp
0x1000ebec4: pushq %r14
0x1000ebec6: pushq %rbx
0x1000ebec7: subq $0xf0, %rsp
0x1000ebece: movq %rdi, -0x18(%rbp)
0x1000ebed2: movq %rdi, -0x40(%rbp)
0x1000ebed6: callq 0x1000efd86 ; symbol stub for: objc_retain
0x1000ebedb: movq -0x40(%rbp), %rdi
0x1000ebedf: movq %rax, -0x48(%rbp)
0x1000ebee3: callq 0x1000efd8c ; symbol stub for: object_getClass
0x1000ebee8: movq 0x5161(%rip), %rdi ; (void *)0x00000001006cb8b0: direct type metadata for Swift.Int
0x1000ebeef: addq $0x8, %rdi
0x1000ebef6: movq 0x5163(%rip), %r8 ; (void *)0x00000001006c5158: protocol witness table for Swift.Int : Swift.ForwardIndex
0x1000ebefd: movq 0x5164(%rip), %rcx ; (void *)0x00000001006c5120: protocol witness table for Swift.Int : Swift._SignedInteger
0x1000ebf04: movq 0x5165(%rip), %rdx ; (void *)0x00000001006c4f90: protocol witness table for Swift.Int : Swift._BuiltinIntegerLiteralConvertible
0x1000ebf0b: leaq -0x38(%rbp), %rsi
0x1000ebf0f: leaq -0x28(%rbp), %r9
0x1000ebf13: leaq -0x20(%rbp), %r10
0x1000ebf17: movq -0x40(%rbp), %r11
0x1000ebf1b: movq %rdi, -0x50(%rbp)
0x1000ebf1f: movq %r11, %rdi
0x1000ebf22: movq %r10, -0x58(%rbp)
0x1000ebf26: movq %r8, -0x60(%rbp)
0x1000ebf2a: movq %rcx, -0x68(%rbp)
0x1000ebf2e: movq %rdx, -0x70(%rbp)
0x1000ebf32: movq %rsi, -0x78(%rbp)
0x1000ebf36: movq %r9, -0x80(%rbp)
0x1000ebf3a: callq *0x50(%rax)
0x1000ebf3d: movq $0x0, -0x20(%rbp)
0x1000ebf45: movq $0x1, -0x28(%rbp)
0x1000ebf4d: movq -0x58(%rbp), %rsi
0x1000ebf51: movq -0x80(%rbp), %rdi
0x1000ebf55: movq -0x78(%rbp), %r8
0x1000ebf59: movq %rdi, -0x88(%rbp)
0x1000ebf60: movq %r8, %rdi
0x1000ebf63: movq -0x88(%rbp), %r8
0x1000ebf6a: movq %rdx, -0x90(%rbp)
0x1000ebf71: movq %r8, %rdx
0x1000ebf74: movq -0x50(%rbp), %r9
0x1000ebf78: movq %rcx, -0x98(%rbp)
0x1000ebf7f: movq %r9, %rcx
0x1000ebf82: movq -0x60(%rbp), %r8
0x1000ebf86: movq -0x68(%rbp), %r10
0x1000ebf8a: movq %r10, (%rsp)
0x1000ebf8e: movq -0x50(%rbp), %r11
0x1000ebf92: movq %r11, 0x8(%rsp)
0x1000ebf97: movq %r11, 0x10(%rsp)
0x1000ebf9c: movq -0x70(%rbp), %rbx
0x1000ebfa0: movq %rbx, 0x18(%rsp)
0x1000ebfa5: movq %rax, -0xa0(%rbp)
0x1000ebfac: callq 0x1000ecac0 ; Swift... @infix <A : Swift.ForwardIndex>(A, A) -> Swift.Range<A>
0x1000ebfb1: movq 0x5098(%rip), %rax ; (void *)0x00000001006cb8b0: direct type metadata for Swift.Int
0x1000ebfb8: addq $0x8, %rax
0x1000ebfbe: movq -0x38(%rbp), %rdi
0x1000ebfc2: movq -0x30(%rbp), %rsi
0x1000ebfc6: movq -0xa0(%rbp), %rdx
0x1000ebfcd: movq -0x90(%rbp), %rcx
0x1000ebfd4: movq -0x98(%rbp), %r8
0x1000ebfdb: movq %rax, %r9
0x1000ebfde: callq 0x1000efc72 ; symbol stub for: Swift.Slice.subscript.getter (Swift.Range<Swift.Int>) -> Swift.Slice<A>
0x1000ebfe3: movq 0x5066(%rip), %rsi ; (void *)0x00000001006cb8b0: direct type metadata for Swift.Int
0x1000ebfea: addq $0x8, %rsi
0x1000ebff1: movq %rax, %rdi
0x1000ebff4: movq %rsi, -0xa8(%rbp)
0x1000ebffb: movq %rdx, %rsi
0x1000ebffe: movq %rcx, %rdx
0x1000ec001: movq -0xa8(%rbp), %rcx
0x1000ec008: callq 0x1000ec910 ; Swift.Slice.__conversion <A>(Swift.Slice<A>)() -> Swift.CConstVoidPointer
0x1000ec00d: movq -0x40(%rbp), %rdi
0x1000ec011: movq %rax, -0xb0(%rbp)
0x1000ec018: movq %rdx, -0xb8(%rbp)
0x1000ec01f: callq 0x1000efd86 ; symbol stub for: objc_retain
0x1000ec024: movq -0x40(%rbp), %rcx
0x1000ec028: movq %rcx, %rdi
0x1000ec02b: movq %rax, -0xc0(%rbp)
0x1000ec032: callq 0x1000efd8c ; symbol stub for: object_getClass
0x1000ec037: movq 0x5012(%rip), %rcx ; (void *)0x00000001006cb8b0: direct type metadata for Swift.Int
0x1000ec03e: addq $0x8, %rcx
0x1000ec045: movq -0x40(%rbp), %rdi
0x1000ec049: movq %rcx, -0xc8(%rbp)
0x1000ec050: callq *0x58(%rax)
0x1000ec053: movq %rax, %rdi
0x1000ec056: movq %rdx, %rsi
0x1000ec059: movq %rcx, %rdx
0x1000ec05c: movq -0xc8(%rbp), %rcx
0x1000ec063: callq 0x1000ec910 ; Swift.Slice.__conversion <A>(Swift.Slice<A>)() -> Swift.CConstVoidPointer
0x1000ec068: movq -0xb8(%rbp), %rcx
0x1000ec06f: cmpq %rdx, %rcx
0x1000ec072: sete %r14b
0x1000ec076: movq %rax, %rdi
0x1000ec079: movb %r14b, -0xc9(%rbp)
0x1000ec080: callq 0x1000efd80 ; symbol stub for: objc_release
0x1000ec085: movq -0xb0(%rbp), %rdi
0x1000ec08c: callq 0x1000efd80 ; symbol stub for: objc_release
0x1000ec091: movq -0x40(%rbp), %rdi
0x1000ec095: callq 0x1000efd86 ; symbol stub for: objc_retain
0x1000ec09a: movb -0xc9(%rbp), %r14b
0x1000ec0a1: movzbl %r14b, %edi
0x1000ec0a5: movq %rax, -0xd8(%rbp)
0x1000ec0ac: callq 0x1000efdb0 ; symbol stub for: ObjectiveC._convertBoolToObjCBool (Swift.Bool) -> ObjectiveC.ObjCBool
0x1000ec0b1: movq 0x5660(%rip), %rsi ; "setB1:"
0x1000ec0b8: movq -0x40(%rbp), %rcx
0x1000ec0bc: movq %rcx, %rdi
0x1000ec0bf: movzbl %al, %edx
0x1000ec0c2: callq 0x1000efd6e ; symbol stub for: objc_msgSend
0x1000ec0c7: movq -0x40(%rbp), %rdi
0x1000ec0cb: callq 0x1000efd80 ; symbol stub for: objc_release
0x1000ec0d0: movq -0x40(%rbp), %rdi
0x1000ec0d4: callq 0x1000efd80 ; symbol stub for: objc_release
0x1000ec0d9: addq $0xf0, %rsp
0x1000ec0e0: popq %rbx
0x1000ec0e1: popq %r14
0x1000ec0e3: popq %rbp
0x1000ec0e4: retq
I am not expert of assembly code, but a quick scan through the generated comment, I can't see ==
anywhere.
Also you can these in the assembly
0x1000ec008: callq 0x1000ec910 ; Swift.Slice.__conversion <A>(Swift.Slice<A>)() -> Swift.CConstVoidPointer
0x1000ec063: callq 0x1000ec910 ; Swift.Slice.__conversion <A>(Swift.Slice<A>)() -> Swift.CConstVoidPointer
so I think it is comparing the underlying pointer value instead of the actual content of the slices
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With