Swift自定义优先级组运算优先级异常是否为Bug?
这可不是Swift的Bug哦,是个很有意思的运算符解析细节!咱们结合你的代码和Swift的规则一步步拆解清楚:
precedencegroup PowerPrecedence { lowerThan: BitwiseShiftPrecedence higherThan: AdditionPrecedence associativity: right } infix operator ^^ : PowerPrecedence extension Int { static func ^^ (lhs: Int, rhs: Int) -> Int { var result = 1 for _ in 1...rhs { result *= lhs } return result } } print(3 ^^ 2 * 3) // 报错 print(2 ^^ 2 ^^ 2 * 2) // 输出32(右结合时)
核心逻辑:歧义的触发条件
Swift只会在不同优先级组的运算符共享同一个操作数,且无法通过结合性明确运算顺序时,才会抛出Adjacent operators are in unordered precedence groups错误。如果表达式能通过结合性拆分成无歧义的子表达式,就不会报错。
1. 右结合时的两条语句
第一条语句:3 ^^ 2 * 3
这个表达式的结构是操作数 ^^ 操作数 * 操作数,中间的2是^^和*的共享操作数。由于你定义的PowerPrecedence和Swift内置的MultiplicationPrecedence没有任何优先级关联(既没设lowerThan也没设higherThan),Swift根本不知道该先算3^^2还是2*3——完全歧义,所以触发编译错误,这和你预期的一致。
第二条语句:2 ^^ 2 ^^ 2 * 2
当^^是右结合时,连续的同一个运算符会优先按照结合性规则处理:2 ^^ 2 ^^ 2会被解析为2 ^^ (2 ^^ 2),这是一个无歧义的链式运算(同一个运算符的右结合逻辑),算出结果16。此时整个表达式简化成了16 * 2——这就是普通的乘法,不存在共享操作数的歧义,所以能正常编译运行,输出32。
2. 改为左结合时的情况
如果你把associativity改成left,第二条语句2 ^^ 2 ^^ 2 * 2就会出现歧义:
左结合规则下,Swift会尝试先处理左边的^^,但此时第二个2同时是第一个^^的右操作数、第二个^^的左操作数;而第三个2又同时是第二个^^的右操作数、*的左操作数。Swift无法判断:
- 是先算
(2^^2) ^^ 2再乘以2? - 还是先算
2*(2^^2)再用2求幂?
因为PowerPrecedence和MultiplicationPrecedence依然没有优先级关联,这种歧义没法通过左结合消除,所以两条语句都会触发编译错误。
最后总结
这完全是Swift运算符解析规则的正常表现:右结合的链式运算帮你把^^部分先打包成一个完整的子表达式,避免了和*共享操作数的歧义;而左结合时,这种歧义无法避免,因此报错。
内容的提问来源于stack exchange,提问作者user2158697

