diff --git a/include/swift/Sema/Constraint.h b/include/swift/Sema/Constraint.h index 7e63e3f6fae49..2f4dc15bc2b78 100644 --- a/include/swift/Sema/Constraint.h +++ b/include/swift/Sema/Constraint.h @@ -263,6 +263,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. @@ -302,8 +304,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 c7e16f4b3a5bc..1e8a9224d8619 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6611,7 +6611,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()) { @@ -6629,7 +6630,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 142baee43af40..a2e54e4e99c3d 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6895,6 +6895,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 d416cd065fd3f..bb8a51a2b92bf 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6589,9 +6589,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); + } + } } } @@ -12222,6 +12231,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 6dddb57ae5213..d56143b63e256 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 aa4872bc76b35..1344cb71cdc51 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 +}