From b5a475a9ef7ccc9c7a13b793a646b45e76c2b9f3 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 24 May 2022 09:57:44 -0700 Subject: [PATCH] [TypeChecker] SE-0324: Extend Swift -> C pointer conversions to `inout` Fixes an oversight where `inout` -> C pointer conversion wasn't covered by implementation of new pointer conversion semantics proposed by SE-0324. Resolves: rdar://92583588 (cherry picked from commit 0a5b3f07270828cd5d6b45eb4a74c5021cc2db1e) --- include/swift/Sema/Constraint.h | 6 ++- lib/Sema/CSApply.cpp | 5 ++- lib/Sema/CSDiagnostics.cpp | 1 + lib/Sema/CSSimplify.cpp | 40 ++++++++++++++++++- lib/Sema/Constraint.cpp | 2 + lib/Sema/ConstraintSystem.cpp | 3 +- .../Inputs/c_pointer_conversions.h | 5 +++ .../swift_to_c_pointer_conversions.swift.gyb | 27 +++++++++++++ 8 files changed, 83 insertions(+), 6 deletions(-) diff --git a/include/swift/Sema/Constraint.h b/include/swift/Sema/Constraint.h index c737934f73731..d85caa2cecc56 100644 --- a/include/swift/Sema/Constraint.h +++ b/include/swift/Sema/Constraint.h @@ -264,6 +264,8 @@ enum class ConversionRestrictionKind { ProtocolMetatypeToProtocolClass, /// Inout-to-pointer conversion. InoutToPointer, + /// Converting from `inout` to a C pointer has `PointerToCPointer` semantics. + InoutToCPointer, /// Array-to-pointer conversion. ArrayToPointer, /// String-to-pointer conversion. @@ -303,8 +305,8 @@ enum class ConversionRestrictionKind { /// via an implicit Double initializer call passing a CGFloat value. CGFloatToDouble, /// Implicit conversion between Swift and C pointers: - // - Unsafe[Mutable]RawPointer -> Unsafe[Mutable]Pointer<[U]Int> - // - Unsafe[Mutable]Pointer <-> Unsafe[Mutable]Pointer + /// - Unsafe[Mutable]RawPointer -> Unsafe[Mutable]Pointer<[U]Int> + /// - Unsafe[Mutable]Pointer <-> Unsafe[Mutable]Pointer PointerToCPointer, // Convert a pack into a type with an equivalent arity. // - If the arity of the pack is 1, drops the pack structure => T diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 0bf11a0ae25d6..1577f43a6b9a9 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6646,7 +6646,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false, locator); } - case ConversionRestrictionKind::InoutToPointer: { + case ConversionRestrictionKind::InoutToPointer: + case ConversionRestrictionKind::InoutToCPointer: { bool isOptional = false; Type unwrappedTy = toType; if (Type unwrapped = toType->getOptionalObjectType()) { @@ -6664,7 +6665,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType)); return result; } - + case ConversionRestrictionKind::ArrayToPointer: { bool isOptional = false; Type unwrappedTy = toType; diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index f8dc362650090..ced5b4f9ee444 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6897,6 +6897,7 @@ void NonEphemeralConversionFailure::emitSuggestionNotes() const { break; } case ConversionRestrictionKind::InoutToPointer: + case ConversionRestrictionKind::InoutToCPointer: // For an arbitrary inout-to-pointer, we can suggest // withUnsafe[Mutable][Bytes/Pointer]. if (auto alternative = getAlternativeKind()) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 9d22543a8d2f0..b70c5ba62506d 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6591,9 +6591,18 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // Only try an inout-to-pointer conversion if we know it's not // an array being converted to a raw pointer type. Such // conversions can only use array-to-pointer. - if (!baseIsArray || !isRawPointerKind(pointerKind)) + if (!baseIsArray || !isRawPointerKind(pointerKind)) { conversionsOrFixes.push_back( ConversionRestrictionKind::InoutToPointer); + + // If regular inout-to-pointer conversion doesn't work, + // let's try C pointer conversion that has special semantics + // for imported declarations. + if (isArgumentOfImportedDecl(locator)) { + conversionsOrFixes.push_back( + ConversionRestrictionKind::InoutToCPointer); + } + } } } @@ -12211,6 +12220,35 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( case ConversionRestrictionKind::PointerToCPointer: return simplifyPointerToCPointerRestriction(type1, type2, flags, locator); + case ConversionRestrictionKind::InoutToCPointer: { + SmallVector optionals; + + auto ptr2 = + type2->getDesugaredType()->lookThroughAllOptionalTypes(optionals); + + increaseScore(SK_ValueToOptional, optionals.size()); + + PointerTypeKind pointerKind; + (void)ptr2->getAnyPointerElementType(pointerKind); + + auto baseType1 = type1->getInOutObjectType(); + + Type ptr1; + // The right-hand size is a raw pointer, so let's use `UnsafeMutablePointer` + // for the `inout` type. + if (pointerKind == PTK_UnsafeRawPointer || + pointerKind == PTK_UnsafeMutableRawPointer) { + ptr1 = BoundGenericType::get(Context.getUnsafeMutablePointerDecl(), + /*parent=*/nullptr, {baseType1}); + } else { + ptr1 = baseType1->wrapInPointer(pointerKind); + } + + assert(ptr1); + + return simplifyPointerToCPointerRestriction(ptr1, ptr2, flags, locator); + } + // T < U or T is bridged to V where V < U ===> Array case ConversionRestrictionKind::ArrayUpcast: { Type baseType1 = *isArrayType(type1); diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 1dcef91a86354..7610dddf9722b 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -612,6 +612,8 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) { return "[string-to-pointer]"; case ConversionRestrictionKind::InoutToPointer: return "[inout-to-pointer]"; + case ConversionRestrictionKind::InoutToCPointer: + return "[inout-to-c-pointer]"; case ConversionRestrictionKind::PointerToPointer: return "[pointer-to-pointer]"; case ConversionRestrictionKind::PointerToCPointer: diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1e3f0d158a598..f9d2b0df07d19 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -5719,7 +5719,8 @@ ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion, case ConversionRestrictionKind::StringToPointer: // Always ephemeral. return ConversionEphemeralness::Ephemeral; - case ConversionRestrictionKind::InoutToPointer: { + case ConversionRestrictionKind::InoutToPointer: + case ConversionRestrictionKind::InoutToCPointer: { // Ephemeral, except if the expression is a reference to a global or // static stored variable, or a directly accessed stored property on such a diff --git a/test/Constraints/Inputs/c_pointer_conversions.h b/test/Constraints/Inputs/c_pointer_conversions.h index 52a0d2bd83b36..c0f4cabd21c19 100644 --- a/test/Constraints/Inputs/c_pointer_conversions.h +++ b/test/Constraints/Inputs/c_pointer_conversions.h @@ -2,6 +2,11 @@ #include "stdint.h" +void void_ptr_func(void * _Nonnull buffer); +void const_void_ptr_func(const void * _Nonnull buffer); +void opt_void_ptr_func(void * _Nullable buffer); +void const_opt_void_ptr_func(const void * _Nullable buffer); + void char_ptr_func(char * _Nonnull buffer); void const_char_ptr_func(const char * _Nonnull buffer); diff --git a/test/Constraints/swift_to_c_pointer_conversions.swift.gyb b/test/Constraints/swift_to_c_pointer_conversions.swift.gyb index d70a6497a86ab..b57cdbf364304 100644 --- a/test/Constraints/swift_to_c_pointer_conversions.swift.gyb +++ b/test/Constraints/swift_to_c_pointer_conversions.swift.gyb @@ -269,3 +269,30 @@ func test_tailored_diagnostic(ptr: UnsafeRawPointer, tptr: UnsafePointer) opt_arg_func(optrU8) // expected-error@-1 {{cannot convert value of type 'UnsafePointer?' to expected argument type 'UnsafePointer?' because local function 'opt_arg_func' was not imported from C header}} } + +func test_inout_to_pointer_conversion() { +% for Size in ['16', '32', '64']: + var x${Size}: Int${Size} = 0 + + void_ptr_func(&x${Size}) // Ok + const_void_ptr_func(&x${Size}) // Ok + opt_void_ptr_func(&x${Size}) // Ok + + char_ptr_func(&x${Size}) // Ok + opt_char_ptr_func(&x${Size}) // Ok + + const_char_ptr_func(&x${Size}) // Ok + const_opt_char_ptr_func(&x${Size}) // Ok + + int_${Size}_ptr_func(&x${Size}) // Ok + uint_${Size}_ptr_func(&x${Size}) // Ok + + opt_int_${Size}_ptr_func(&x${Size}) // Ok + opt_uint_${Size}_ptr_func(&x${Size}) // Ok + + const_int_${Size}_ptr_func(&x${Size}) // OK + const_uint_${Size}_ptr_func(&x${Size}) // OK + const_opt_int_${Size}_ptr_func(&x${Size}) // OK + const_opt_uint_${Size}_ptr_func(&x${Size}) // OK +% end +}