Files
swift-mirror/test/Unsafe/safe.swift
Doug Gregor b9cb5ce791 [SE-0458] Disambiguate "unsafe" expression within string interpolation
String interpolation uses an end-of-file token, which we weren't
checking for. Fixes rdar://146493296
2025-03-07 15:10:27 -08:00

244 lines
6.7 KiB
Swift

// RUN: %target-typecheck-verify-swift -strict-memory-safety -print-diagnostic-groups
// The feature flag should be enabled.
#if !hasFeature(StrictMemorySafety)
#error("Strict memory safety is not enabled!")
#endif
@unsafe
func unsafeFunction() { }
@unsafe
struct UnsafeType { }
func f() {
unsafe unsafeFunction()
}
func g() {
unsafe unsafeFunction()
}
func h(_: UnsafeType) {
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
unsafeFunction() // expected-note{{reference to unsafe global function 'unsafeFunction()'}}
// okay
unsafe unsafeFunction()
// expected-warning@+1{{no unsafe operations occur within 'unsafe' expression}}
unsafe g()
}
func rethrowing(body: (UnsafeType) throws -> Void) rethrows { }
class HasStatics {
static internal func f(_: UnsafeType) { }
}
@unsafe
func unsafeInt() -> Int { 5 }
struct HasProperties {
var computed: Int {
unsafe unsafeInt()
}
@unsafe var computedUnsafe: Int {
unsafe unsafeInt()
}
static var blah: Int = {
unsafe unsafeInt()
}()
@unsafe static var blahUnsafe: Int = {
unsafe unsafeInt()
}()
}
protocol P { }
extension Int: @unsafe P { }
func acceptP(_: some P) { }
func testConformance(i: Int) {
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
acceptP(i) // expected-note{{@unsafe conformance of 'Int' to protocol 'P' involves unsafe code}}
}
func returnsOpaqueP() -> some P {
5 // expected-warning{{expression uses unsafe constructs but is not marked with 'unsafe'}}
// expected-note@-1{{@unsafe conformance of 'Int' to protocol 'P' involves unsafe code}}
}
func returnsExistentialP() -> any P {
5 // expected-warning{{expression uses unsafe constructs but is not marked with 'unsafe'}}
// expected-note@-1{{@unsafe conformance of 'Int' to protocol 'P' involves unsafe code}}
}
// FIXME: Should work even if the IteratorProtocol conformance is safe
struct UnsafeAsSequence: @unsafe Sequence, @unsafe IteratorProtocol {
@unsafe mutating func next() -> Int? { nil }
}
func testUnsafeAsSequenceForEach() {
let uas = UnsafeAsSequence()
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}{{12-12=unsafe }}
for _ in uas { } // expected-note{{conformance}}
// expected-warning@-1{{for-in loop uses unsafe constructs but is not marked with 'unsafe' [StrictMemorySafety]}}{{7-7=unsafe }}
for _ in unsafe uas { } // expected-warning{{for-in loop uses unsafe constructs but is not marked with 'unsafe' [StrictMemorySafety]}}{{7-7=unsafe }}
for unsafe _ in unsafe uas { } // okay
}
func testForInUnsafeAmbiguity(_ integers: [Int]) {
for unsafe in integers {
_ = unsafe
}
for unsafe: Int in integers {
_ = unsafe
}
}
struct UnsafeIterator: @unsafe IteratorProtocol {
@unsafe mutating func next() -> Int? { nil }
}
struct SequenceWithUnsafeIterator: Sequence {
func makeIterator() -> UnsafeIterator { UnsafeIterator() }
}
func testUnsafeIteratorForEach() {
let swui = SequenceWithUnsafeIterator()
for _ in swui { } // expected-warning{{for-in loop uses unsafe constructs but is not marked with 'unsafe'}}{{7-7=unsafe }}
for unsafe _ in swui { } // okay, it's only the iterator that's unsafe
}
class MyRange {
@unsafe init(unchecked bounds: Range<Int>) { }
convenience init(_ bounds: Range<Int>) {
// bounds check
self.init(unchecked: bounds) // expected-warning{{expression uses unsafe constructs but is not marked with 'unsafe'}}
// expected-note@-1{{reference to unsafe initializer 'init(unchecked:)'}}
}
}
func casting(value: Any, i: Int) {
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
_ = value as? UnsafeType // expected-note{{reference to unsafe type 'UnsafeType'}}
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
_ = value as! UnsafeType // expected-note{{reference to unsafe type 'UnsafeType'}}
_ = unsafe value as? UnsafeType
_ = unsafe value as! UnsafeType
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
_ = i as any P // expected-note{{@unsafe conformance of 'Int' to protocol 'P' involves unsafe code}}
}
func metatypes() {
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
let _: Any.Type = UnsafeType.self // expected-note{{reference to unsafe type 'UnsafeType'}}
let _: Any.Type = unsafe UnsafeType.self
}
func testKeyPath() {
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
_ = \HasProperties.computedUnsafe // expected-note{{reference to unsafe property 'computedUnsafe'}}
_ = unsafe \HasProperties.computedUnsafe
}
func takesAutoclosure<T>(_ body: @autoclosure () -> T) { }
func testAutoclosure() {
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}{{3-3=unsafe }}
takesAutoclosure(unsafeFunction()) // expected-note{{reference to unsafe global function 'unsafeFunction()'}}
unsafe takesAutoclosure(unsafeFunction())
takesAutoclosure(unsafe unsafeFunction())
}
// Parsing of `unsafe` expressions.
func testUnsafePositionError() -> Int {
return 3 + unsafe unsafeInt() // expected-error{{'unsafe' cannot appear to the right of a non-assignment operator}}
}
enum Color {
case red
}
func acceptBools(_: Bool, _: Bool) { }
func acceptBoolsUnsafeLabel(unsafe _: Bool, _: Bool) { }
func unsafe(_: Int) { }
func unsafeFun() {
var unsafe = true
unsafe = false
unsafe.toggle()
_ = [unsafe]
_ = { unsafe }
acceptBools(unsafe, unsafe)
acceptBoolsUnsafeLabel(unsafe: unsafe, unsafe)
let color: Color
// expected-warning@+1{{no unsafe operations occur within 'unsafe' expression}}
color = unsafe .red
_ = color
if unsafe { }
}
func moreUnsafeFunc(unsafe: [Int]) {
let _: [Int] = unsafe []
// expected-warning@-1{{no unsafe operations occur within 'unsafe' expression}}
_ = unsafe[1]
_ = "\(unsafe)"
}
func yetMoreUnsafeFunc(unsafe: () -> Void) {
unsafe()
_ = unsafe ()
// expected-warning@-1{{no unsafe operations occur within 'unsafe' expression}}
}
// @safe suppresses unsafe-type-related diagnostics on an entity
struct MyArray<Element> {
@safe func withUnsafeBufferPointer<R, E>(
_ body: (UnsafeBufferPointer<Element>) throws(E) -> R
) throws(E) -> R {
return unsafe try body(.init(start: nil, count: 0))
}
}
extension UnsafeBufferPointer {
@unsafe var unsafeCount: Int { 17 }
@safe var safeCount: Int { unsafe unsafeCount }
}
func testMyArray(ints: MyArray<Int>) {
ints.withUnsafeBufferPointer { buffer in
let bufferCopy = unsafe buffer
_ = unsafe bufferCopy
print(buffer.safeCount)
unsafe print(buffer.unsafeCount)
}
}