blob: d22735c501093a01fff584e15017394ecda38013 [file] [log] [blame]
// RUN: %target-swift-frontend -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..<count {
yield stored // expected-error {{accessor must not yield more than once}}
// expected-note@-1 {{previous yield was here}}
}
}
_modify {
yield &stored // expected-note {{previous yield was here}}
for _ in 0..<count {
yield &stored // expected-error {{accessor must not yield more than once}}
}
}
}
}
enum CompassPoint {
case north
case south
case east
case west
}
struct TestYieldInSwitch {
var stored: Int
var cp: CompassPoint
var computed: Int {
get { return stored }
_modify {
switch cp {
case .north:
yield &stored
case .south:
stored = 10
case .east:
yield &stored
case .west:
stored = 12 // expected-note {{missing yield in the 'west' case}}
}
cp = .north
} // expected-error {{accessor must yield on all paths before returning}}
}
}
struct TestYieldInSwitchWithFallThrough {
var stored: Int
var cp: CompassPoint
var computed: Int {
_read {
switch cp {
case .north:
fallthrough
case .south:
fallthrough
case .east:
fallthrough
case .west:
yield stored
}
}
_modify {
switch cp {
case .north:
fallthrough
case .south:
yield &stored
case .east:
fallthrough // expected-note {{missing yield in the 'east' case}}
case .west:
stored = 12
}
} // expected-error {{accessor must yield on all paths before returning}}
}
}
struct TestYieldInSwitchWithoutCaseNames {
var stored: Int
var cp: CompassPoint
var computed: Int {
_read {
switch cp {
case .north:
yield stored
case _:
break // expected-note {{missing yield in this case}}
}
} // expected-error {{accessor must yield on all paths before returning}}
_modify {
switch cp {
case .north:
yield &stored
case _ where stored < 0: // expected-note {{missing yield in this case}}
stored = 12
case .south:
fallthrough
case .east:
fallthrough
case .west:
yield &stored
}
} // expected-error {{accessor must yield on all paths before returning}}
}
var computed2: Int {
_read {
switch cp {
case .north:
yield stored
case _ where stored < 0: // expected-note {{missing yield when the condition is false}}
yield stored
default:
break
}
} // expected-error {{accessor must yield on all paths before returning}}
_modify {
switch cp {
case .north:
yield &stored
case _ where stored < 0: // expected-note {{missing yield in this case}}
break
default:
yield &stored
}
} // expected-error {{accessor must yield on all paths before returning}}
}
var computed3: Int {
_read {
switch cp {
case .north:
yield stored
case _ where stored < 0: // expected-note {{missing yield in this case}}
break
case .south:
fallthrough
case .east:
yield stored
case .west:
break
}
} // expected-error {{accessor must yield on all paths before returning}}
_modify {
switch cp {
case .north:
break
case _ where stored < 0: // expected-note {{missing yield when the condition is true}}
break
default:
yield &stored
}
} // expected-error {{accessor must yield on all paths before returning}}
}
}
struct TestExplicitReturn {
var stored: Int
var flag: Bool
var computed: Int {
mutating _read {
if stored > 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<L, R> {
case left(L)
case right(R)
}
var intOrBool: Either<Int, Bool>
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 TestMutipleTries {
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 TestMutipleCatches {
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 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<T> {
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<T> {
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<T> {
var stored: T?
var computed: T {
_read {
yield stored!
}
}
}