mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Add a new mandatory BooleanLiteralFolding pass which constant folds conditional branches with boolean literals as operands.
```
%1 = integer_literal -1
%2 = apply %bool_init(%1) // Bool.init(_builtinBooleanLiteral:)
%3 = struct_extract %2, #Bool._value
cond_br %3, bb1, bb2
```
->
```
...
br bb1
```
This pass is intended to run before DefiniteInitialization, where mandatory inlining and constant folding didn't run, yet (which would perform this kind of optimization).
This optimization is required to let DefiniteInitialization handle boolean literals correctly.
For example in infinite loops:
```
init() {
while true { // DI need to know that there is no loop exit from this while-statement
if some_condition {
member_field = init_value
break
}
}
}
```
315 lines
7.0 KiB
Swift
315 lines
7.0 KiB
Swift
// RUN: %target-swift-frontend -emit-sil %s -o /dev/null -verify
|
|
|
|
// REQUIRES: swift_in_compiler
|
|
|
|
func a() {
|
|
a() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
func throwing_func() throws {
|
|
try throwing_func() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
func b(_ x : Int) {
|
|
if x != 0 {
|
|
b(x) // expected-warning {{function call causes an infinite recursion}}
|
|
} else {
|
|
b(x+1) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
|
|
func noInvariantArgs(_ x : Int) {
|
|
if x != 0 {
|
|
noInvariantArgs(x-1) // expected-warning {{function call causes an infinite recursion}}
|
|
} else {
|
|
noInvariantArgs(x+1) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
|
|
func c(_ x : Int) {
|
|
if x != 0 {
|
|
c(5)
|
|
}
|
|
}
|
|
|
|
func invariantArgCondition(_ x : Int) {
|
|
if x != 0 {
|
|
invariantArgCondition(x) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
|
|
func invariantLoopCondition(_ x : Int) {
|
|
while x != 0 {
|
|
invariantLoopCondition(x) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
|
|
final class ClassWithInt {
|
|
var i: Int = 0
|
|
}
|
|
|
|
func invariantMemCondition(_ c: ClassWithInt) {
|
|
if c.i > 0 {
|
|
invariantMemCondition(c) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
|
|
func variantMemCondition(_ c: ClassWithInt) {
|
|
if c.i > 0 {
|
|
c.i -= 1
|
|
invariantMemCondition(c) // no warning
|
|
}
|
|
}
|
|
|
|
func nestedInvariantCondition(_ x : Int, _ y: Int) {
|
|
if x > 0 {
|
|
if y != 0 {
|
|
if x == 0 {
|
|
nestedInvariantCondition(x, y) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func nestedVariantCondition(_ x : Int, _ y: Int) {
|
|
if x > 0 {
|
|
if y != 0 {
|
|
if x == 0 {
|
|
nestedVariantCondition(x, y - 1) // no warning
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func multipleArgs1(_ x : Int, _ y : Int) {
|
|
if y > 0 {
|
|
multipleArgs1(x - 1, y) // expected-warning {{function call causes an infinite recursion}}
|
|
} else if x > 10 {
|
|
multipleArgs1(x - 2, y)
|
|
}
|
|
}
|
|
|
|
func multipleArgs2(_ x : Int, _ y : Int) {
|
|
if y > 0 {
|
|
multipleArgs2(x, y - 1) // expected-warning {{function call causes an infinite recursion}}
|
|
} else if x > 10 {
|
|
multipleArgs2(x, y - 2) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
|
|
func multipleArgsNoWarning(_ x : Int, _ y : Int) {
|
|
if y > 0 {
|
|
multipleArgsNoWarning(x, y - 1)
|
|
} else if x > 10 {
|
|
multipleArgsNoWarning(x - 1, y)
|
|
}
|
|
}
|
|
|
|
struct A {}
|
|
struct B {}
|
|
|
|
func deadEndBlockInElseBranch(_ x : Int) {
|
|
if x != 0 {
|
|
deadEndBlockInElseBranch(x - 1) // no warning
|
|
} else {
|
|
_ = unsafeBitCast(A(), to: B.self)
|
|
}
|
|
}
|
|
|
|
|
|
struct Str {
|
|
var x = 27
|
|
|
|
mutating func writesMemory() {
|
|
if x > 0 {
|
|
x -= 1
|
|
writesMemory() // no warning
|
|
}
|
|
}
|
|
|
|
mutating func doesNotWriteMem() {
|
|
if x > 0 {
|
|
doesNotWriteMem() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
|
|
func nonMutating() {
|
|
if x > 0 {
|
|
nonMutating() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
}
|
|
|
|
func d(_ x : Int) {
|
|
var x = x
|
|
if x != 0 {
|
|
x += 1
|
|
}
|
|
return d(x) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
// Doesn't warn on mutually recursive functions
|
|
|
|
func e() { f() }
|
|
func f() { e() }
|
|
|
|
func g() {
|
|
while true {
|
|
g() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
g()
|
|
}
|
|
|
|
func h(_ x : Int) {
|
|
while (x < 5) {
|
|
h(x+1)
|
|
}
|
|
}
|
|
|
|
func i(_ x : Int) {
|
|
var x = x
|
|
while (x < 5) {
|
|
x -= 1
|
|
}
|
|
i(0) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
func j() -> Int {
|
|
return 5 + j() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
func k() -> Any {
|
|
return type(of: k()) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
@_silgen_name("exit") func exit(_: Int32) -> Never
|
|
|
|
func l() {
|
|
guard Bool.random() else {
|
|
exit(0) // no warning; calling 'exit' terminates the program
|
|
}
|
|
l()
|
|
}
|
|
|
|
func m() {
|
|
guard Bool.random() else {
|
|
fatalError() // we _do_ warn here, because fatalError is a programtermination_point
|
|
}
|
|
m() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
enum MyNever {}
|
|
|
|
func blackHole() -> MyNever {
|
|
blackHole() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
@_semantics("programtermination_point")
|
|
func terminateMe() -> MyNever {
|
|
terminateMe() // no warning; terminateMe is a programtermination_point
|
|
}
|
|
|
|
func n() -> MyNever {
|
|
if Bool.random() {
|
|
blackHole() // no warning; blackHole() will terminate the program
|
|
}
|
|
n()
|
|
}
|
|
|
|
func o() -> MyNever {
|
|
if Bool.random() {
|
|
o()
|
|
}
|
|
blackHole() // no warning; blackHole() will terminate the program
|
|
}
|
|
|
|
func mayHaveSideEffects() {}
|
|
|
|
func p() {
|
|
if Bool.random() {
|
|
mayHaveSideEffects() // presence of side-effects doesn't alter the check for the programtermination_point apply
|
|
fatalError()
|
|
}
|
|
p() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
class S {
|
|
convenience init(a: Int) {
|
|
self.init(a: a) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
init(a: Int?) {}
|
|
|
|
static func a() {
|
|
return a() // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
func b() {
|
|
var i = 0
|
|
repeat {
|
|
i += 1
|
|
b() // expected-warning {{function call causes an infinite recursion}}
|
|
} while (i > 5)
|
|
}
|
|
|
|
var bar: String = "hi!"
|
|
}
|
|
|
|
class T: S {
|
|
// No warning, calls super
|
|
override func b() {
|
|
var i = 0
|
|
repeat {
|
|
i += 1
|
|
super.b()
|
|
} while (i > 5)
|
|
}
|
|
|
|
override var bar: String {
|
|
get {
|
|
return super.bar
|
|
}
|
|
set {
|
|
self.bar = newValue // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class U {
|
|
required convenience init(x: Int) {
|
|
self.init(x: x) // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
}
|
|
|
|
func == (l: S?, r: S?) -> Bool {
|
|
if l == nil && r == nil { return true } // expected-warning {{function call causes an infinite recursion}}
|
|
guard let l = l, let r = r else { return false }
|
|
return l === r
|
|
}
|
|
|
|
public func == <Element>(lhs: Array<Element>, rhs: Array<Element>) -> Bool {
|
|
return lhs == rhs // expected-warning {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
func factorial(_ n : UInt) -> UInt {
|
|
return (n != 0) ? factorial(n - 1) * n : factorial(1) // expected-warning {{function call causes an infinite recursion}}
|
|
// expected-warning @-1 {{function call causes an infinite recursion}}
|
|
|
|
}
|
|
|
|
func tr(_ key: String) -> String {
|
|
return tr(key) ?? key // expected-warning {{left side of nil coalescing operator '??' has non-optional type}}
|
|
// expected-warning @-1 {{function call causes an infinite recursion}}
|
|
}
|
|
|
|
class Node {
|
|
var parent: Node?
|
|
var rootNode: RootNode {
|
|
return parent!.rootNode // No warning - has an override.
|
|
}
|
|
}
|
|
|
|
class RootNode: Node {
|
|
override var rootNode: RootNode { return self }
|
|
}
|