Skip to content

Commit e4eb271

Browse files
committed
[CSSimplify] Implement ArrayToCPointer conversion
Extends implementation to support conversions between arrays and pointers that are allowed by the SE-0324 proposal. Resolved: rdar://99089335
1 parent 438ee33 commit e4eb271

File tree

3 files changed

+87
-40
lines changed

3 files changed

+87
-40
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7389,6 +7389,14 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
73897389
if (type1->isArrayType()) {
73907390
conversionsOrFixes.push_back(
73917391
ConversionRestrictionKind::ArrayToPointer);
7392+
7393+
// If regular array-to-pointer conversion doesn't work,
7394+
// let's try C pointer conversion that has special semantics
7395+
// for imported declarations.
7396+
if (isArgumentOfImportedDecl(locator)) {
7397+
conversionsOrFixes.push_back(
7398+
ConversionRestrictionKind::ArrayToCPointer);
7399+
}
73927400
}
73937401

73947402
// The pointer can be converted from a string, if the element
@@ -13436,8 +13444,30 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
1343613444
case ConversionRestrictionKind::PointerToCPointer:
1343713445
return simplifyPointerToCPointerRestriction(type1, type2, flags, locator);
1343813446

13439-
case ConversionRestrictionKind::ArrayToCPointer:
13440-
llvm_unreachable("not yet implemented");
13447+
case ConversionRestrictionKind::ArrayToCPointer: {
13448+
auto ptr2 = type2->getDesugaredType()->lookThroughAllOptionalTypes();
13449+
13450+
PointerTypeKind pointerKind;
13451+
auto cPtr = ptr2->getAnyPointerElementType(pointerKind);
13452+
13453+
// If the parameter is a raw pointer or its element type is not a
13454+
// supported (un-)signed integer it implies a regular ArrayToPointer
13455+
// conversion.
13456+
if (isRawPointerKind(pointerKind) ||
13457+
!(cPtr->isInt() || cPtr->isUInt() ||
13458+
cPtr->isInt8() || cPtr->isUInt8() ||
13459+
cPtr->isInt16() || cPtr->isUInt16() ||
13460+
cPtr->isInt32() || cPtr->isUInt32() ||
13461+
cPtr->isInt64() || cPtr->isUInt64())) {
13462+
return SolutionKind::Error;
13463+
}
13464+
13465+
increaseScore(SK_ValueToPointerConversion);
13466+
13467+
type1 = getFixedTypeRecursive(type1->getInOutObjectType()->isArrayType(),
13468+
/*wantRValue=*/false);
13469+
LLVM_FALLTHROUGH;
13470+
}
1344113471

1344213472
case ConversionRestrictionKind::InoutToCPointer: {
1344313473
SmallVector<Type, 2> optionals;
@@ -13702,12 +13732,7 @@ ConstraintSystem::simplifyPointerToCPointerRestriction(
1370213732
Type type1, Type type2, TypeMatchOptions flags,
1370313733
ConstraintLocatorBuilder locator) {
1370413734
bool inCorrectPosition = isArgumentOfImportedDecl(locator);
13705-
13706-
if (inCorrectPosition) {
13707-
// Make sure that solutions with implicit pointer conversions
13708-
// are always worse than the ones without them.
13709-
increaseScore(SK_ImplicitValueConversion);
13710-
} else {
13735+
if (!inCorrectPosition) {
1371113736
// If this is not an imported function, let's not proceed with
1371213737
// the conversion, unless in diagnostic mode.
1371313738
if (!shouldAttemptFixes())
@@ -13728,6 +13753,10 @@ ConstraintSystem::simplifyPointerToCPointerRestriction(
1372813753
assert(cPtr);
1372913754

1373013755
auto markSupported = [&]() -> SolutionKind {
13756+
// Make sure that solutions with implicit pointer conversions
13757+
// are always worse than the ones without them.
13758+
increaseScore(SK_ImplicitValueConversion);
13759+
1373113760
if (inCorrectPosition)
1373213761
return SolutionKind::Solved;
1373313762

@@ -13739,6 +13768,10 @@ ConstraintSystem::simplifyPointerToCPointerRestriction(
1373913768
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1374013769
};
1374113770

13771+
// If pointers have the same element type there is nothing to do.
13772+
if (swiftPtr->isEqual(cPtr))
13773+
return markSupported();
13774+
1374213775
// Unsafe[Mutable]RawPointer -> Unsafe[Mutable]Pointer<[U]Int8>
1374313776
if (swiftPtrKind == PTK_UnsafeRawPointer ||
1374413777
swiftPtrKind == PTK_UnsafeMutableRawPointer) {
@@ -13761,7 +13794,6 @@ ConstraintSystem::simplifyPointerToCPointerRestriction(
1376113794

1376213795
// Unsafe[Mutable]Pointer<Int{8, 16, ...}> <->
1376313796
// Unsafe[Mutable]Pointer<UInt{8, 16, ...}>
13764-
1376513797
if (swiftPtr->isInt() || swiftPtr->isUInt()) {
1376613798
addConstraint(ConstraintKind::Equal, cPtr,
1376713799
swiftPtr->isUInt() ? ctx.getIntType() : ctx.getUIntType(),

test/Constraints/swift_to_c_pointer_conversions.swift.gyb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,37 @@ func test_inout_to_pointer_conversion() {
296296
const_opt_uint_${Size}_ptr_func(&x${Size}) // OK
297297
% end
298298
}
299+
300+
func test_array_to_pointer_conversion() {
301+
% for Size in ['16', '32', '64']:
302+
var x${Size}: [Int${Size}] = []
303+
304+
void_ptr_func(&x${Size}) // Ok
305+
const_void_ptr_func(x${Size}) // Ok
306+
opt_void_ptr_func(x${Size})
307+
// expected-error@-1 {{cannot convert value of type '[Int${Size}]' to expected argument type 'UnsafeMutableRawPointer?'}}
308+
309+
char_ptr_func(x${Size})
310+
// expected-error@-1 {{cannot convert value of type '[Int${Size}]' to expected argument type 'UnsafeMutablePointer<CChar>' (aka 'UnsafeMutablePointer<Int8>')}}
311+
opt_char_ptr_func(x${Size})
312+
// expected-error@-1 {{cannot convert value of type '[Int${Size}]' to expected argument type 'UnsafeMutablePointer<CChar>?' (aka 'Optional<UnsafeMutablePointer<Int8>>')}}
313+
314+
const_char_ptr_func(x${Size}) // Ok
315+
const_opt_char_ptr_func(x${Size}) // Ok
316+
317+
int_${Size}_ptr_func(x${Size})
318+
// expected-error@-1 {{cannot convert value of type '[Int${Size}]' to expected argument type 'UnsafeMutablePointer<Int${Size}>'}}
319+
uint_${Size}_ptr_func(x${Size})
320+
// expected-error@-1 {{cannot convert value of type '[Int${Size}]' to expected argument type 'UnsafeMutablePointer<UInt${Size}>'}}
321+
322+
opt_int_${Size}_ptr_func(x${Size})
323+
// expected-error@-1 {{cannot convert value of type '[Int${Size}]' to expected argument type 'UnsafeMutablePointer<Int${Size}>?'}}
324+
opt_uint_${Size}_ptr_func(x${Size})
325+
// expected-error@-1 {{cannot convert value of type '[Int${Size}]' to expected argument type 'UnsafeMutablePointer<UInt${Size}>?'}}
326+
327+
const_int_${Size}_ptr_func(x${Size}) // OK
328+
const_uint_${Size}_ptr_func(x${Size}) // OK
329+
const_opt_int_${Size}_ptr_func(x${Size}) // OK
330+
const_opt_uint_${Size}_ptr_func(x${Size}) // OK
331+
% end
332+
}

test/SILGen/diagnose_implicit_raw_conversion_unsupported.swift

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -40,44 +40,25 @@ func test_unsupported<T>(arg: T) {
4040
// expected-note@-1 {{arguments to generic parameter 'Pointee' ('UInt8' and 'Int8') are expected to be equal}}
4141
}
4242

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() {
43+
// Array<T> to C pointer conversion is supported under SE-0324
44+
func test_array_to_c_pointer_concrete() {
5245
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}}
46+
read_uchar(constIntArray)
5547

5648
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}}
49+
read_char(constUIntArray)
5950

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}}
51+
var intArray: [Int8] = [0] // expected-warning {{variable 'intArray' was never mutated; consider changing to 'let' constant}}
52+
read_uchar(intArray)
6353

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}}
54+
var uintArray: [UInt8] = [0] // expected-warning {{variable 'uintArray' was never mutated; consider changing to 'let' constant}}
55+
read_char(uintArray)
6756

6857
}
6958

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) {
59+
// Array<T> to C pointer conversion is supported under SE-0324
60+
func test_array_to_c_pointer_generic<T>(arg: T) {
7861
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}}
62+
read_char(constArray)
63+
read_uchar(constArray)
8364
}

0 commit comments

Comments
 (0)