Unexpected logical evaluation of predicate condition in filter(_:)

I have been trying to use Predicate macro with filter(_:) method on an Array to obtain a filtered set of instances, but I am not getting the expected result.

import Foundation

public struct Todo {
  let isCompleted: Bool
}

let todos = [
  Todo(isCompleted: true),
  Todo(isCompleted: false)
]

print(try todos.filter(#Predicate { $0.isCompleted == true }))
print(try todos.filter(#Predicate { $0.isCompleted == false })) 
print(todos.filter { $0.isCompleted == false })

The result is:

$ swift build & swift run
[predicates.Todo(isCompleted: true)]
[predicates.Todo(isCompleted: true)]
[predicates.Todo(isCompleted: false)]

In the above example, the second filtered todos array is including Todo instances where the isCompleted property is true, which should be false as same as the last one.
#Predicate is working strangely when there are 2 or more in the same context.

Is this behavior a language specification issue, or is my understanding incorrect? Could anyone please explain the cause of this issue and suggest an appropriate solution?

my environment is here.

$ swift --version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0

I would appreciate your guidance on this matter.

I do not reproduce the wrong filtering by running your code on iOS 17.4 simulator, and on macOS 14.3.1 (from the tests of some project that happened to be there on my screen).

It would be useful to narrow down a reliable reproduction context, and report the bug to Apple on http://feedbackassistant.apple.com if you think it hasn't been fixed (this is an issue with Apple's Foundation, not with the Swift language).

Meanwhile, this sentence of yours:

...makes me think of Predicate expression gets wrong type when a different predicate is defined · Issue #70365 · apple/swift · GitHub.

The workaround might be to isolate those predicates (in different "contexts", whatever it means for the macro).

Thank you for your comment and suggestion.
The suggested issue is similar my situation though, I will report my issue.
Thanks again!

I'm able to reproduce this bug in swift repl using swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4) Target: arm64-apple-macosx14.0.

The minimum requirement to reproduce it is to have at least to #Predicate macros in the same scope.

My guess is that the problem is in the macro system since the expansion of each #Predicate macro is correct.

import Foundation

struct Todo {
  let name: String
  let isCompleted: Bool
}

let todos = [
  Todo(name: "first", isCompleted: true),
  Todo(name: "second", isCompleted: false)
]

let filteredTodosUsingMacro1 = try todos.filter(#Predicate { $0.isCompleted == true })
let filteredTodosUsingMacro2 = try todos.filter(#Predicate { $0.isCompleted == false })

let filteredTodos1 = try todos.filter(
  Predicate({
    PredicateExpressions.build_Equal(
      lhs: PredicateExpressions.build_KeyPath(
        root: PredicateExpressions.build_Arg(
          $0
        ),
        keyPath: \.isCompleted
      ),
      rhs: PredicateExpressions.build_Arg(
        true
      )
    )
  })
)

let filteredTodos2 = try todos.filter(
  Predicate({
    PredicateExpressions.build_Equal(
      lhs: PredicateExpressions.build_KeyPath(
        root: PredicateExpressions.build_Arg(
          $0
        ),
        keyPath: \.isCompleted
      ),
      rhs: PredicateExpressions.build_Arg(
        false
      )
    )
  })
)

Thanks for taking a time to reproduce it.

I've been trying more experiments with a simpler example and got an interesting result.

print(try [0, 1, 2].filter(#Predicate { $0 == 0 }))
print(try [0, 1, 2].filter(#Predicate { $0 == 1 }))

do {
  print(try [0, 1, 2].filter(#Predicate { $0 == 0 }))
  print(try [0, 1, 2].filter(#Predicate { $0 == 1 }))
}

result is here.

[0]
[0]
[0]
[1]

The second one should be [1] as same as the last one.
I think there is a problem with global definition.