mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
SIL cannot be assumed to be 100% valid if errors were detected in diagnostic passes. For example in DefiniteInitialization. Fixes some false verifier errors in case -sil-verify-all is used. rdar://127519229 rdar://127518886
731 lines
16 KiB
Swift
731 lines
16 KiB
Swift
// 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..<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 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<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!
|
|
}
|
|
}
|
|
}
|