Skip to content

Commit 290b07d

Browse files
authored
Merge pull request #64249 from xedin/rdar-99089335
[TypeChecker] SE-0324: Extend Swift -> C pointer conversions to arrays
2 parents 584919e + a7fbc04 commit 290b07d

File tree

8 files changed

+111
-46
lines changed

8 files changed

+111
-46
lines changed

include/swift/Sema/Constraint.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ enum class ConversionRestrictionKind {
282282
InoutToCPointer,
283283
/// Array-to-pointer conversion.
284284
ArrayToPointer,
285+
/// Converting from array to a C pointer has `PointerToCPointer` semantics.
286+
ArrayToCPointer,
285287
/// String-to-pointer conversion.
286288
StringToPointer,
287289
/// Pointer-to-pointer conversion.

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6879,7 +6879,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
68796879
return result;
68806880
}
68816881

6882-
case ConversionRestrictionKind::ArrayToPointer: {
6882+
case ConversionRestrictionKind::ArrayToPointer:
6883+
case ConversionRestrictionKind::ArrayToCPointer: {
68836884
bool isOptional = false;
68846885
Type unwrappedTy = toType;
68856886
if (Type unwrapped = toType->getOptionalObjectType()) {

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7367,7 +7367,8 @@ void NonEphemeralConversionFailure::emitSuggestionNotes() const {
73677367

73687368
// Then try to find a suitable alternative.
73697369
switch (ConversionKind) {
7370-
case ConversionRestrictionKind::ArrayToPointer: {
7370+
case ConversionRestrictionKind::ArrayToPointer:
7371+
case ConversionRestrictionKind::ArrayToCPointer: {
73717372
// Don't suggest anything for optional arrays, as there's currently no
73727373
// direct alternative.
73737374
if (getArgType()->getOptionalObjectType())

lib/Sema/CSSimplify.cpp

Lines changed: 56 additions & 13 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,6 +13444,31 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
1343613444
case ConversionRestrictionKind::PointerToCPointer:
1343713445
return simplifyPointerToCPointerRestriction(type1, type2, flags, locator);
1343813446

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+
}
13471+
1343913472
case ConversionRestrictionKind::InoutToCPointer: {
1344013473
SmallVector<Type, 2> optionals;
1344113474

@@ -13699,12 +13732,7 @@ ConstraintSystem::simplifyPointerToCPointerRestriction(
1369913732
Type type1, Type type2, TypeMatchOptions flags,
1370013733
ConstraintLocatorBuilder locator) {
1370113734
bool inCorrectPosition = isArgumentOfImportedDecl(locator);
13702-
13703-
if (inCorrectPosition) {
13704-
// Make sure that solutions with implicit pointer conversions
13705-
// are always worse than the ones without them.
13706-
increaseScore(SK_ImplicitValueConversion);
13707-
} else {
13735+
if (!inCorrectPosition) {
1370813736
// If this is not an imported function, let's not proceed with
1370913737
// the conversion, unless in diagnostic mode.
1371013738
if (!shouldAttemptFixes())
@@ -13725,6 +13753,10 @@ ConstraintSystem::simplifyPointerToCPointerRestriction(
1372513753
assert(cPtr);
1372613754

1372713755
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+
1372813760
if (inCorrectPosition)
1372913761
return SolutionKind::Solved;
1373013762

@@ -13736,6 +13768,23 @@ ConstraintSystem::simplifyPointerToCPointerRestriction(
1373613768
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1373713769
};
1373813770

13771+
auto elementLoc = locator.withPathElement(LocatorPathElt::GenericArgument(0));
13772+
13773+
if (swiftPtr->isTypeVariableOrMember()) {
13774+
// Inference between the equivalent pointer kinds is
13775+
// handled by regular pointer conversions.
13776+
if (swiftPtrKind == cPtrKind)
13777+
return SolutionKind::Error;
13778+
13779+
addConstraint(ConstraintKind::BindToPointerType, swiftPtr, cPtr,
13780+
elementLoc);
13781+
return markSupported();
13782+
}
13783+
13784+
// If pointers have the same element type there is nothing to do.
13785+
if (swiftPtr->isEqual(cPtr))
13786+
return markSupported();
13787+
1373913788
// Unsafe[Mutable]RawPointer -> Unsafe[Mutable]Pointer<[U]Int8>
1374013789
if (swiftPtrKind == PTK_UnsafeRawPointer ||
1374113790
swiftPtrKind == PTK_UnsafeMutableRawPointer) {
@@ -13747,18 +13796,12 @@ ConstraintSystem::simplifyPointerToCPointerRestriction(
1374713796
// Unsafe[Mutable]Pointer<T> -> Unsafe[Mutable]Pointer<[U]Int8>
1374813797
if (cPtr->isInt8() || cPtr->isUInt8()) {
1374913798
// <T> can default to the type of C pointer.
13750-
addConstraint(
13751-
ConstraintKind::Defaultable, swiftPtr, cPtr,
13752-
locator.withPathElement(LocatorPathElt::GenericArgument(0)));
13799+
addConstraint(ConstraintKind::Defaultable, swiftPtr, cPtr, elementLoc);
1375313800
return markSupported();
1375413801
}
1375513802

13756-
auto elementLoc =
13757-
locator.withPathElement(LocatorPathElt::GenericArgument(0));
13758-
1375913803
// Unsafe[Mutable]Pointer<Int{8, 16, ...}> <->
1376013804
// Unsafe[Mutable]Pointer<UInt{8, 16, ...}>
13761-
1376213805
if (swiftPtr->isInt() || swiftPtr->isUInt()) {
1376313806
addConstraint(ConstraintKind::Equal, cPtr,
1376413807
swiftPtr->isUInt() ? ctx.getIntType() : ctx.getUIntType(),

lib/Sema/Constraint.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,8 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) {
656656
return "[protocol-metatype-to-object]";
657657
case ConversionRestrictionKind::ArrayToPointer:
658658
return "[array-to-pointer]";
659+
case ConversionRestrictionKind::ArrayToCPointer:
660+
return "[array-to-c-pointer]";
659661
case ConversionRestrictionKind::StringToPointer:
660662
return "[string-to-pointer]";
661663
case ConversionRestrictionKind::InoutToPointer:

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6238,6 +6238,7 @@ ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion,
62386238
ConstraintLocatorBuilder locator) {
62396239
switch (conversion) {
62406240
case ConversionRestrictionKind::ArrayToPointer:
6241+
case ConversionRestrictionKind::ArrayToCPointer:
62416242
case ConversionRestrictionKind::StringToPointer:
62426243
// Always ephemeral.
62436244
return ConversionEphemeralness::Ephemeral;

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)