// RUN: %target-swift-frontend -sil-verify-all -emit-sil -primary-file %s -o /dev/null -verify // // Tests for yield-once diagnostics emitted for generalized accessors. struct TestNoYield { var computed: Int { _read { } // expected-error {{accessor must yield before returning}} _modify { } // expected-error {{accessor must yield before returning}} } } struct TestReturnPathWithoutYield { var stored: Int var flag: Bool var computed: Int { mutating _read { if flag { // expected-note {{missing yield when the condition is false}} yield stored } flag = true } // expected-error {{accessor must yield on all paths before returning}} _modify { // Diagnostics should attach a note to the earliest conflicting branch. if flag { // expected-note {{missing yield when the condition is false}} yield &stored } if !flag { flag = true } } // expected-error {{accessor must yield on all paths before returning}} } } struct TestIfElseWithoutYield { var stored: Int var flag: Bool var computed: Int { mutating _read { if flag { // expected-note {{missing yield when the condition is false}} yield stored } else { flag = true } } // expected-error {{accessor must yield on all paths before returning}} _modify { if flag { // expected-note {{missing yield when the condition is true}} flag = true } else { yield &stored } } // expected-error {{accessor must yield on all paths before returning}} } } struct TestNestedIfs { var stored: Int var flag: Bool // Diagnostics should attach a note to the innermost conflicting branch, which // is the point of first conflict. var computed: Int { _read { if flag { if stored > 0 { // expected-note {{missing yield when the condition is false}} yield stored } } } // expected-error {{accessor must yield on all paths before returning}} _modify { if flag { if stored > 0 { // expected-note {{missing yield when the condition is true}} } else { yield &stored } } flag = true } // expected-error {{accessor must yield on all paths before returning}} } } struct TestMultipleYields { var stored: Int var flag: Bool var computed: Int { _read { if flag { yield stored // expected-note {{previous yield was here}} } yield stored // expected-error {{accessor must not yield more than once}} } _modify { yield &stored // expected-note {{previous yield was here}} if flag { yield &stored // expected-error {{accessor must not yield more than once}} } } } } struct TestMultipleYields2 { var stored: Int var flag: Bool var computed: Int { _read { if flag { yield stored return } yield stored } _modify { if flag { yield &stored } else { yield &stored } } } } struct TestConvolutedPaths { var flag: Bool var stored : Int var computed: Int { _read { if flag { yield stored // expected-note {{previous yield was here}} } if !flag { yield stored // expected-error {{accessor must not yield more than once}} } } _modify { if flag { yield &stored // expected-note {{previous yield was here}} } if !flag { yield &stored // expected-error {{accessor must not yield more than once}} } } } } struct TestYieldInLoops { var stored: Int var count: Int var computed: Int { _read { for _ in 0.. 0 { // expected-note {{missing yield when the condition is true}} return } if flag { yield stored } else { yield stored } flag = true } // expected-error {{accessor must yield on all paths before returning}} _modify { if stored > 0 { return } if stored == 0 { // expected-note {{missing yield when the condition is false}} if flag { yield &stored } else { yield &stored } return } flag = true } // expected-error {{accessor must yield on all paths before returning}} } var anotherProp: Int { mutating _read { if flag { stored = 2 } return; // expected-error {{accessor must yield before returning}} if !flag { // expected-warning {{code after 'return' will never be executed}} stored = 3 } } } } struct TestYieldsInGuards { var storedOpt: Int? var defaultStorage: Int var computed: Int { _read { guard let stored = storedOpt else { yield defaultStorage return } yield stored } } } struct TestYieldsInGuards2 { var flag: Bool var defaultStorage: Int var computed: Int { _read { guard flag else { // expected-note {{missing yield when the condition is false}} return } yield defaultStorage } // expected-error {{accessor must yield on all paths before returning}} _modify { guard flag else { // expected-note {{missing yield when the condition is true}} yield &defaultStorage return } defaultStorage += 1 } // expected-error {{accessor must yield on all paths before returning}} } } struct TestYieldsInLetPatterns { var storedOpt: Int? var defaultStorage: Int var computed: Int { _read { if let stored = storedOpt { // expected-note {{missing yield in the nil case}} yield stored } } // expected-error {{accessor must yield on all paths before returning}} _modify { if var stored = storedOpt { // expected-note {{missing yield in the non-nil case}} stored += 1 return } yield &defaultStorage } // expected-error {{accessor must yield on all paths before returning}} } enum Either { case left(L) case right(R) } var intOrBool: Either var computed2: Int { _read { if case .left(let x) = intOrBool { // expected-note {{missing yield in the 'right' case}} yield x } } // expected-error {{accessor must yield on all paths before returning}} _modify { guard let stored = storedOpt else { // expected-note {{missing yield in the non-nil case}} yield &defaultStorage return } storedOpt = stored + 1 } // expected-error {{accessor must yield on all paths before returning}} } var cp: CompassPoint var computed3: Int { _read { if case .north = cp { // expected-note {{missing yield in this case}} yield 0 } } // expected-error {{accessor must yield on all paths before returning}} } } enum SoftError : Error { case Ignorable } func aThrowingFunction() throws { throw SoftError.Ignorable } struct TestYieldInDoCatch { var stored: Int var computed: Int { _read { do { try aThrowingFunction() yield stored } catch { yield stored } } } } struct TestYieldInDoCatch2 { var stored: Int var computed: Int { _read { do { try aThrowingFunction() // expected-note {{missing yield when error is thrown}} yield stored } catch { } } // expected-error {{accessor must yield on all paths before returning}} _modify { do { try aThrowingFunction() // expected-note {{missing yield when error is not thrown}} } catch { yield &stored } } // expected-error {{accessor must yield on all paths before returning}} } } enum Binary { case one case two } struct TestMultipleTries { var stored: Int var computed: Int { _read { do { try aThrowingFunction() yield stored // expected-note {{previous yield was here}} try aThrowingFunction() yield stored // expected-error {{accessor must not yield more than once}} } catch { } } _modify { do { try aThrowingFunction() yield &stored try aThrowingFunction() // expected-note {{missing yield when error is thrown}} } catch { } } // expected-error {{accessor must yield on all paths before returning}} } var flag: Bool var bin: Binary var computed2: Int { _read { do { if flag { try aThrowingFunction() } else { try aThrowingFunction() // expected-note {{missing yield when error is thrown}} } yield stored } catch { } } // expected-error {{accessor must yield on all paths before returning}} _modify { do { switch bin { case .one: try aThrowingFunction() yield &stored case .two: try aThrowingFunction() // expected-note {{missing yield when error is thrown}} yield &stored } } catch { } } // expected-error {{accessor must yield on all paths before returning}} } var computed3: Int { _read { do { if flag { try aThrowingFunction() } else { try aThrowingFunction() // expected-note {{missing yield when error is not thrown}} } } catch { yield stored } } // expected-error {{accessor must yield on all paths before returning}} _modify { do { switch bin { case .one: try aThrowingFunction() case .two: try aThrowingFunction() // expected-note {{missing yield when error is not thrown}} } } catch { yield &stored } } // expected-error {{accessor must yield on all paths before returning}} } } struct TestMultipleCatches { enum NumError: Error { case One case Two case Three } var stored: Int var computed: Int { _read { // This is a very interesting test, where the error is on the try. // It could have been better if it is on the switch case in the // error case. do { try aThrowingFunction() // expected-note {{missing yield when error is thrown}} yield stored } catch NumError.One { yield stored } catch NumError.Two { yield stored } catch NumError.Three { } catch { yield stored } } // expected-error {{accessor must yield on all paths before returning}} _modify { do { try aThrowingFunction() // expected-note {{missing yield when error is not thrown}} } catch NumError.One { yield &stored } catch NumError.Two { yield &stored } catch NumError.Three { } catch { yield &stored } } // expected-error {{accessor must yield on all paths before returning}} } } // Test for labeled breaks. struct TestYieldWithLabeledBreaks { var stored: Int var flag: Bool var bin: Binary var computed: Int { _read { ifstmt: if flag { switch bin { case .one: yield stored case .two: break ifstmt // expected-note {{missing yield in the 'two' case}} } } } // expected-error {{accessor must yield on all paths before returning}} _modify { loop: while flag { switch bin { case .one: yield &stored // expected-error {{accessor must not yield more than once}} // expected-note@-1 {{previous yield was here}} case .two: break loop } } } } } // Test switches over non-enum values. struct TestYieldInSwitchWhere { var stored: Int var computed: Int { _read { switch stored { case let x where x > 0: yield stored case let x where x < 0: // expected-note {{missing yield when the condition is true}} return default: yield 3 } } // expected-error {{accessor must yield on all paths before returning}} } } struct TestYieldInSwitchValue { var stored: Bool var computed: Int { _read { switch stored { case true: yield 0 case false: return // expected-note {{missing yield in the 2nd case}} } } // expected-error {{accessor must yield on all paths before returning}} } } struct TestYieldInSwitchEnumAddr { var storedOpt: T? var computed: T { _read { if let stored = storedOpt { yield stored } // expected-note {{missing yield in the nil case}} } // expected-error {{accessor must yield on all paths before returning}} } } // Test checked casts. struct TestYieldInCheckedCast { var stored: T var computed: Int { _read { if let x = (stored as? Int) { yield x } // expected-note {{missing yield in the nil case}} } // expected-error {{accessor must yield on all paths before returning}} } } struct TestYieldInForcedUnwrappingt { var stored: T? var computed: T { _read { yield stored! } } }