On Xcode 16, when trying to compile, getting error "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions" (In expansion of macro 'Predicate' here)
Same code was compiling and working fine on Xcode 15. Removing any two of six parts divided by || compiles fine.
Code:
class FeaturesPredicate {
class func predicate(_ enabledFeaturesName: String) -> Predicate<Caliber> {
#Predicate {
caliber in
caliber.caliberData.featuresABCD.contains {
feature in
enabledFeaturesName == feature.name
} || caliber.caliberData.featuresA.contains {
feature in
enabledFeaturesName == feature.name
} || caliber.caliberData.featuresB.contains {
feature in
enabledFeaturesName == feature.name
} || caliber.caliberData.featuresC.contains {
feature in
enabledFeaturesName == feature.name
} || caliber.caliberData.featuresD.contains {
feature in
enabledFeaturesName == feature.name
} || caliber.caliberData.featuresN.contains {
feature in
enabledFeaturesName == feature.name
}
}
Expanded predicate:
Foundation.Predicate({
caliber in
PredicateExpressions.build_Disjunction(
lhs: PredicateExpressions.build_Disjunction(
lhs: PredicateExpressions.build_Disjunction(
lhs: PredicateExpressions.build_Disjunction(
lhs: PredicateExpressions.build_Disjunction(
lhs: PredicateExpressions.build_contains(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(caliber),
keyPath: \.caliberData
),
keyPath: \.featuresABCD
)
) {
feature in
PredicateExpressions.build_Equal(
lhs: PredicateExpressions.build_Arg(enabledFeaturesName),
rhs: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(feature),
keyPath: \.name
)
)
},
rhs: PredicateExpressions.build_contains(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(caliber),
keyPath: \.caliberData
),
keyPath: \.featuresA
)
) {
feature in
PredicateExpressions.build_Equal(
lhs: PredicateExpressions.build_Arg(enabledFeaturesName),
rhs: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(feature),
keyPath: \.name
)
)
}
),
rhs: PredicateExpressions.build_contains(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(caliber),
keyPath: \.caliberData
),
keyPath: \.featuresB
)
) {
feature in
PredicateExpressions.build_Equal(
lhs: PredicateExpressions.build_Arg(enabledFeaturesName),
rhs: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(feature),
keyPath: \.name
)
)
}
),
rhs: PredicateExpressions.build_contains(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(caliber),
keyPath: \.caliberData
),
keyPath: \.featuresC
)
) {
feature in
PredicateExpressions.build_Equal(
lhs: PredicateExpressions.build_Arg(enabledFeaturesName),
rhs: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(feature),
keyPath: \.name
)
)
}
),
rhs: PredicateExpressions.build_contains(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(caliber),
keyPath: \.caliberData
),
keyPath: \.featuresD
)
) {
feature in
PredicateExpressions.build_Equal(
lhs: PredicateExpressions.build_Arg(enabledFeaturesName),
rhs: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(feature),
keyPath: \.name
)
)
}
),
rhs: PredicateExpressions.build_contains(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(caliber),
keyPath: \.caliberData
),
keyPath: \.featuresN
)
) {
feature in
PredicateExpressions.build_Equal(
lhs: PredicateExpressions.build_Arg(enabledFeaturesName),
rhs: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(feature),
keyPath: \.name
)
)
}
)
})
As the error message says, you should break it down into smaller pieces and then compose them.
Here I have made each of the contains
calls its own separate Predicate
, and I have also broken down the big disjunction into two halves.
class func predicate(_ enabledFeaturesName: String) -> Predicate<Caliber> {
let featuresABCD = #Predicate<Caliber> { caliber in
caliber.caliberData.featuresABCD.contains {
feature in
enabledFeaturesName == feature.name
}
}
let featuresA = #Predicate<Caliber> { caliber in
caliber.caliberData.featuresA.contains {
feature in
enabledFeaturesName == feature.name
}
}
let featuresB = #Predicate<Caliber> { caliber in
caliber.caliberData.featuresB.contains {
feature in
enabledFeaturesName == feature.name
}
}
let featuresC = #Predicate<Caliber> { caliber in
caliber.caliberData.featuresC.contains {
feature in
enabledFeaturesName == feature.name
}
}
let featuresD = #Predicate<Caliber> { caliber in
caliber.caliberData.featuresD.contains {
feature in
enabledFeaturesName == feature.name
}
}
let featuresN = #Predicate<Caliber> { caliber in
caliber.caliberData.featuresN.contains {
feature in
enabledFeaturesName == feature.name
}
}
let firstHalf = #Predicate<Caliber> { caliber in
featuresABCD.evaluate(caliber) ||
featuresA.evaluate(caliber) ||
featuresB.evaluate(caliber)
}
let secondHalf = #Predicate<Caliber> { caliber in
featuresC.evaluate(caliber) ||
featuresD.evaluate(caliber) ||
featuresN.evaluate(caliber)
}
return #Predicate {
caliber in
firstHalf.evaluate(caliber) ||
secondHalf.evaluate(caliber)
}
}
This compiles very fast on my computer. You don't have to break it down this much if you just want it to successfully compile. You can combine firstHalf
and secondHalf
into a single #Predicate
, but it takes considerably longer to compile on my computer.
Note that this uses build_evaluate
which requires iOS 17.4.