Skip to content

Commit 64e1ce2

Browse files
committed
Add unit tests for implicit inout to raw pointer diagnostics.
1 parent bdeb58d commit 64e1ce2

File tree

4 files changed

+283
-0
lines changed

4 files changed

+283
-0
lines changed

test/SILGen/Inputs/readbytes.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
void read_char(const char *input);
2+
void read_uchar(const unsigned char *input);
3+
void read_void(const void *input);
4+
void write_char(char *input);
5+
void write_uchar(unsigned char *input);
6+
void write_void(void *input);
7+
8+
void take_opaque_pointer(const void *);
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// RUN: %target-swift-emit-silgen -import-objc-header %S/Inputs/readbytes.h %s -o /dev/null -verify
2+
//
3+
// Diagnose invalid conversion from an inout argument to a raw pointer.
4+
5+
func readBytes(_ pointer: UnsafeRawPointer) {}
6+
func writeBytes(_ pointer: UnsafeMutableRawPointer) {}
7+
func readInt8(_ pointer: UnsafePointer<Int8>) {}
8+
func writeInt8(_ pointer: UnsafeMutablePointer<Int8>) {}
9+
func readUInt8(_ pointer: UnsafePointer<UInt8>) {}
10+
func writeUInt8(_ pointer: UnsafeMutablePointer<UInt8>) {}
11+
12+
struct Aggregate {
13+
var pointer: UnsafeRawPointer?
14+
var value: Int32
15+
}
16+
17+
class C {
18+
init() {}
19+
}
20+
21+
struct NonTrivial {
22+
var c: C
23+
}
24+
25+
// SILGen diagnostics prohibits these implicit casts:
26+
func test_errors<T>(arg: T, sarg: String) {
27+
var nonTrivial = NonTrivial(c: C())
28+
readBytes(&nonTrivial) // expected-warning {{forming 'UnsafeRawPointer' to a variable of type 'NonTrivial'; this is likely incorrect because 'NonTrivial' may contain an object reference.}}
29+
writeBytes(&nonTrivial) // expected-warning {{forming 'UnsafeMutableRawPointer' to a variable of type 'NonTrivial'; this is likely incorrect because 'NonTrivial' may contain an object reference.}}
30+
31+
var t: T = arg
32+
readBytes(&t) // expected-warning {{forming 'UnsafeRawPointer' to a variable of type 'T'; this is likely incorrect because 'T' may contain an object reference.}}
33+
writeBytes(&t) // expected-warning {{forming 'UnsafeMutableRawPointer' to a variable of type 'T'; this is likely incorrect because 'T' may contain an object reference.}}
34+
read_char(&t) // expected-warning {{forming 'UnsafePointer<Int8>' to a variable of type 'T'; this is likely incorrect because 'T' may contain an object reference.}}
35+
write_char(&t) // expected-warning {{forming 'UnsafeMutablePointer<Int8>' to a variable of type 'T'; this is likely incorrect because 'T' may contain an object reference.}}
36+
read_uchar(&t) // expected-warning {{forming 'UnsafePointer<UInt8>' to a variable of type 'T'; this is likely incorrect because 'T' may contain an object reference.}}
37+
write_uchar(&t) // expected-warning {{forming 'UnsafeMutablePointer<UInt8>' to a variable of type 'T'; this is likely incorrect because 'T' may contain an object reference.}}
38+
39+
let constArray: [T] = [arg]
40+
readBytes(constArray) // expected-warning {{forming 'UnsafeRawPointer' to a variable of type '[T]'; this is likely incorrect because 'T' may contain an object reference.}}
41+
42+
var array: [T] = [arg]
43+
readBytes(&array) // expected-warning {{forming 'UnsafeRawPointer' to a variable of type '[T]'; this is likely incorrect because 'T' may contain an object reference.}}
44+
writeBytes(&array) // expected-warning {{forming 'UnsafeMutableRawPointer' to a variable of type '[T]'; this is likely incorrect because 'T' may contain an object reference.}}
45+
46+
read_char(&array) // expected-warning {{forming 'UnsafePointer<Int8>' to a variable of type 'Array<T>'; this is likely incorrect because 'T' may contain an object reference.}}
47+
write_char(&array) // expected-warning {{forming 'UnsafeMutablePointer<Int8>' to a variable of type 'Array<T>'; this is likely incorrect because 'T' may contain an object reference.}}
48+
read_uchar(&array) // expected-warning {{forming 'UnsafePointer<UInt8>' to a variable of type 'Array<T>'; this is likely incorrect because 'T' may contain an object reference.}}
49+
write_uchar(&array) // expected-warning {{forming 'UnsafeMutablePointer<UInt8>' to a variable of type 'Array<T>'; this is likely incorrect because 'T' may contain an object reference.}}
50+
51+
var string: String = sarg
52+
readBytes(&string) // expected-warning {{forming 'UnsafeRawPointer' to an inout variable of type String exposes the internal representation rather than the string contents.}}
53+
writeBytes(&string) // expected-warning {{forming 'UnsafeMutableRawPointer' to an inout variable of type String exposes the internal representation rather than the string contents.}}
54+
read_char(&string) // expected-warning {{forming 'UnsafePointer<Int8>' to an inout variable of type String exposes the internal representation rather than the string contents.}}
55+
write_char(&string) // expected-warning {{forming 'UnsafeMutablePointer<Int8>' to an inout variable of type String exposes the internal representation rather than the string contents.}}
56+
read_uchar(&string) // expected-warning {{forming 'UnsafePointer<UInt8>' to an inout variable of type String exposes the internal representation rather than the string contents.}}
57+
write_uchar(&string) // expected-warning {{forming 'UnsafeMutablePointer<UInt8>' to an inout variable of type String exposes the internal representation rather than the string contents.}}
58+
}
59+
60+
// Test the explicit cast workaround for all of the above errors
61+
func test_explicit<T>(arg: T, sarg: String) {
62+
var nonTrivial = NonTrivial(c: C())
63+
withUnsafeBytes(of: &nonTrivial) {
64+
readBytes($0.baseAddress!)
65+
}
66+
withUnsafeMutableBytes(of: &nonTrivial) {
67+
writeBytes($0.baseAddress!)
68+
}
69+
70+
var t: T = arg
71+
withUnsafeBytes(of: &t) {
72+
readBytes($0.baseAddress!)
73+
read_char($0.baseAddress!)
74+
read_uchar($0.baseAddress!)
75+
}
76+
withUnsafeMutableBytes(of: &t) {
77+
writeBytes($0.baseAddress!)
78+
write_char($0.baseAddress!)
79+
write_uchar($0.baseAddress!)
80+
}
81+
let constArray: [T] = [arg]
82+
constArray.withUnsafeBytes() {
83+
readBytes($0.baseAddress!)
84+
}
85+
var array: [T] = [arg]
86+
array.withUnsafeBytes() {
87+
readBytes($0.baseAddress!)
88+
read_char($0.baseAddress!)
89+
read_uchar($0.baseAddress!)
90+
}
91+
array.withUnsafeMutableBytes() {
92+
writeBytes($0.baseAddress!)
93+
write_char($0.baseAddress!)
94+
write_uchar($0.baseAddress!)
95+
}
96+
97+
let string: String = sarg
98+
readBytes(string)
99+
read_char(string)
100+
read_uchar(string)
101+
}
102+
103+
// SILGen diagnostics accepts these implicit casts:
104+
func test_accepted<I: FixedWidthInteger>(intArg: I, sarg: String, simdArg: SIMD4<Float>) {
105+
var aggregate = Aggregate(pointer: UnsafeRawPointer(bitPattern: 0), value: 0)
106+
readBytes(&aggregate)
107+
writeBytes(&aggregate)
108+
read_char(&aggregate)
109+
write_char(&aggregate)
110+
read_uchar(&aggregate)
111+
write_uchar(&aggregate)
112+
113+
var int: I = intArg
114+
readBytes(&int)
115+
writeBytes(&int)
116+
read_char(&int)
117+
write_char(&int)
118+
read_uchar(&int)
119+
write_uchar(&int)
120+
121+
let constArray: [I] = [intArg]
122+
readBytes(constArray)
123+
// read_char(constArray) - this case never worked because of a bug in SE-0324
124+
// read_uchar(constArray) - this case never worked because of a bug in SE-0324
125+
126+
var intArray: [I] = [intArg]
127+
readBytes(&intArray)
128+
writeBytes(&intArray)
129+
read_char(&intArray)
130+
write_char(&intArray)
131+
read_uchar(&intArray)
132+
write_uchar(&intArray)
133+
134+
var byteArray: [UInt8] = [0]
135+
readBytes(&byteArray)
136+
writeBytes(&byteArray)
137+
readUInt8(&byteArray)
138+
writeUInt8(&byteArray)
139+
write_char(&byteArray)
140+
read_uchar(byteArray)
141+
write_uchar(&byteArray)
142+
143+
let string: String = sarg
144+
readBytes(string)
145+
readUInt8(string)
146+
read_char(string)
147+
read_uchar(string)
148+
149+
var simd: SIMD4<Float> = simdArg
150+
readBytes(&simd)
151+
writeBytes(&simd)
152+
read_char(&simd)
153+
write_char(&simd)
154+
read_uchar(&simd)
155+
write_uchar(&simd)
156+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-swift-emit-silgen -import-objc-header %S/Inputs/readbytes.h %s -o /dev/null -verify
2+
//
3+
// REQUIRES: objc_interop
4+
//
5+
// Diagnose invalid conversion from an inout Data argument to a raw pointer.
6+
7+
import Foundation
8+
9+
func readBytes(_ pointer: UnsafeRawPointer) {}
10+
func writeBytes(_ pointer: UnsafeMutableRawPointer) {}
11+
12+
// SILGen diagnostics prohibits these implicit casts:
13+
func test_errors(darg: Data) {
14+
var data: Data = darg
15+
readBytes(&data) // expected-warning {{forming 'UnsafeRawPointer' to a variable of type 'Data'; this is likely incorrect because 'Data' may contain an object reference.}}
16+
writeBytes(&data) // expected-warning {{forming 'UnsafeMutableRawPointer' to a variable of type 'Data'; this is likely incorrect because 'Data' may contain an object reference.}}
17+
read_char(&data) // expected-warning {{forming 'UnsafePointer<Int8>' to a variable of type 'Data'; this is likely incorrect because 'Data' may contain an object reference.}}
18+
write_char(&data) // expected-warning {{forming 'UnsafeMutablePointer<Int8>' to a variable of type 'Data'; this is likely incorrect because 'Data' may contain an object reference.}}
19+
read_uchar(&data) // expected-warning {{forming 'UnsafePointer<UInt8>' to a variable of type 'Data'; this is likely incorrect because 'Data' may contain an object reference.}}
20+
write_uchar(&data) // expected-warning {{forming 'UnsafeMutablePointer<UInt8>' to a variable of type 'Data'; this is likely incorrect because 'Data' may contain an object reference.}}
21+
}
22+
23+
// Test the explicit cast workaround for all of the above errors
24+
func test_explicit(darg: Data) {
25+
var data: Data = darg
26+
data.withUnsafeBytes() {
27+
readBytes($0.baseAddress!)
28+
read_char($0.baseAddress!)
29+
read_uchar($0.baseAddress!)
30+
}
31+
data.withUnsafeMutableBytes() {
32+
writeBytes($0.baseAddress!)
33+
write_char($0.baseAddress!)
34+
write_uchar($0.baseAddress!)
35+
}
36+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// RUN: %target-swift-emit-silgen -import-objc-header %S/Inputs/readbytes.h %s -o /dev/null -verify
2+
//
3+
// Diagnose invalid conversion from an inout argument to a raw pointer.
4+
//
5+
// These cases are caught early during Sema, so they are never seen by SILGen's implicit conversion diagnostics.
6+
7+
func readBytes(_ pointer: UnsafeRawPointer) {}
8+
func writeBytes(_ pointer: UnsafeMutableRawPointer) {}
9+
func readInt8(_ pointer: UnsafePointer<Int8>) {}
10+
func writeInt8(_ pointer: UnsafeMutablePointer<Int8>) {}
11+
func readUInt8(_ pointer: UnsafePointer<UInt8>) {}
12+
func writeUInt8(_ pointer: UnsafeMutablePointer<UInt8>) {}
13+
14+
// These implicit casts never worked and will continue to be unsupported.
15+
func test_unsupported<T>(arg: T) {
16+
var t: T = arg
17+
readInt8(&t) // expected-error {{cannot convert value of type 'UnsafePointer<T>' to expected argument type 'UnsafePointer<Int8>'}}
18+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('T' and 'Int8') are expected to be equal}}
19+
writeInt8(&t) // expected-error {{cannot convert value of type 'UnsafeMutablePointer<T>' to expected argument type 'UnsafeMutablePointer<Int8>'}}
20+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('T' and 'Int8') are expected to be equal}}
21+
readUInt8(&t) // expected-error {{cannot convert value of type 'UnsafePointer<T>' to expected argument type 'UnsafePointer<UInt8>'}}
22+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('T' and 'UInt8') are expected to be equal}}
23+
writeUInt8(&t) // expected-error {{cannot convert value of type 'UnsafeMutablePointer<T>' to expected argument type 'UnsafeMutablePointer<UInt8>'}}
24+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('T' and 'UInt8') are expected to be equal}}
25+
26+
let constArray: [UInt8] = [0]
27+
readInt8(constArray) // expected-error {{cannot convert value of type 'UnsafePointer<UInt8>' to expected argument type 'UnsafePointer<Int8>'}}
28+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('UInt8' and 'Int8') are expected to be equal}}
29+
30+
// Mutating a const array obviously does not work, no need to show these
31+
// in the proposal
32+
writeBytes(constArray) // expected-error {{cannot convert value of type '[UInt8]' to expected argument type 'UnsafeMutableRawPointer'}}
33+
writeInt8(constArray) // expected-error {{cannot convert value of type '[UInt8]' to expected argument type 'UnsafeMutablePointer<Int8>'}}
34+
writeUInt8(constArray) // expected-error {{cannot convert value of type '[UInt8]' to expected argument type 'UnsafeMutablePointer<UInt8>'}}
35+
36+
var byteArray: [UInt8] = [0]
37+
readInt8(&byteArray) // expected-error {{cannot convert value of type 'UnsafePointer<UInt8>' to expected argument type 'UnsafePointer<Int8>'}}
38+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('UInt8' and 'Int8') are expected to be equal}}
39+
writeInt8(&byteArray) // expected-error {{cannot convert value of type 'UnsafeMutablePointer<UInt8>' to expected argument type 'UnsafeMutablePointer<Int8>'}}
40+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('UInt8' and 'Int8') are expected to be equal}}
41+
}
42+
43+
// These implicit casts should work according to
44+
// [SE-0324: Relax diagnostics for pointer arguments to C functions]
45+
// (https://github.com/apple/swift-evolution/blob/main/proposals/0324-c-lang-pointer-arg-conversion.md)
46+
// They currently raise a "cannot convert value" error because of
47+
// the `UInt8` vs. `Int8` mismatch.
48+
//
49+
// If we decide to support these as bug-fixes for SE-0324, then the
50+
// implicit inout-to-raw conversion should also accept them.
51+
func test_se0324_accept() {
52+
let constIntArray: [Int8] = [0]
53+
read_uchar(constIntArray) // expected-error {{cannot convert value of type 'UnsafePointer<Int8>' to expected argument type 'UnsafePointer<UInt8>'}}
54+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Int8' and 'UInt8') are expected to be equal}}
55+
56+
let constUIntArray: [UInt8] = [0]
57+
read_char(constUIntArray) // expected-error {{cannot convert value of type 'UnsafePointer<UInt8>' to expected argument type 'UnsafePointer<CChar>' (aka 'UnsafePointer<Int8>')}}
58+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('UInt8' and 'CChar' (aka 'Int8')) are expected to be equal}}
59+
60+
var intArray: [Int8] = [0]
61+
read_uchar(intArray) // expected-error {{cannot convert value of type 'UnsafePointer<Int8>' to expected argument type 'UnsafePointer<UInt8>'}}
62+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('Int8' and 'UInt8') are expected to be equal}}
63+
64+
var uintArray: [UInt8] = [0]
65+
read_char(uintArray) // expected-error {{cannot convert value of type 'UnsafePointer<UInt8>' to expected argument type 'UnsafePointer<CChar>' (aka 'UnsafePointer<Int8>')}}
66+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('UInt8' and 'CChar' (aka 'Int8')) are expected to be equal}}
67+
68+
}
69+
70+
// These implicit casts should work according to
71+
// SE-0324: Relax diagnostics for pointer arguments to C functions]
72+
// They currently raise a "cannot convert value" error because of
73+
// the `UInt8` vs. `Int8` mismatch.
74+
//
75+
// If we decide to support these as bug-fixes for SE-0324, then the
76+
// implicit inout-to-raw conversion should issue a warning instead.
77+
func test_se0324_error<T>(arg: T) {
78+
let constArray: [T] = [arg]
79+
read_char(constArray) // expected-error {{cannot convert value of type 'UnsafePointer<T>' to expected argument type 'UnsafePointer<CChar>' (aka 'UnsafePointer<Int8>')}}
80+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('T' and 'CChar' (aka 'Int8')) are expected to be equal}}
81+
read_uchar(constArray) // expected-error {{cannot convert value of type 'UnsafePointer<T>' to expected argument type 'UnsafePointer<UInt8>'}}
82+
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('T' and 'UInt8') are expected to be equal}}
83+
}

0 commit comments

Comments
 (0)