Skip to content

Commit 1c0a306

Browse files
authored
Merge pull request #37956 from xedin/implicit-swift-to-c-ptr-conversions
[TypeChecker] Implement limited set of conversions between Swift and C pointers
2 parents 5ebb1b2 + a9fdb7a commit 1c0a306

15 files changed

+640
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,11 @@ ERROR(cannot_convert_argument_value,none,
362362
"cannot convert value of type %0 to expected argument type %1",
363363
(Type,Type))
364364

365+
ERROR(cannot_convert_argument_value_for_swift_func,none,
366+
"cannot convert value of type %0 to expected argument type %1 "
367+
"because %2 %3 was not imported from C header",
368+
(Type,Type, DescriptiveDeclKind, DeclName))
369+
365370
NOTE(candidate_has_invalid_argument_at_position,none,
366371
"candidate expects %select{|in-out }2value of type %0 for parameter #%1 (got %3)",
367372
(Type, unsigned, bool, Type))

include/swift/Sema/CSFix.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ enum class FixKind : uint8_t {
336336
/// resolved.
337337
SpecifyTypeForPlaceholder,
338338

339+
/// Allow Swift -> C pointer conversion in an argument position
340+
/// of a Swift function.
341+
AllowSwiftToCPointerConversion,
342+
339343
/// Allow `weak` declarations to be bound to a non-optional type.
340344
AllowNonOptionalWeak,
341345

@@ -2791,6 +2795,22 @@ class AllowNonOptionalWeak final : public ConstraintFix {
27912795
}
27922796
};
27932797

2798+
class AllowSwiftToCPointerConversion final : public ConstraintFix {
2799+
AllowSwiftToCPointerConversion(ConstraintSystem &cs,
2800+
ConstraintLocator *locator)
2801+
: ConstraintFix(cs, FixKind::AllowSwiftToCPointerConversion, locator) {}
2802+
2803+
public:
2804+
std::string getName() const override {
2805+
return "allow implicit Swift -> C pointer conversion";
2806+
}
2807+
2808+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2809+
2810+
static AllowSwiftToCPointerConversion *create(ConstraintSystem &cs,
2811+
ConstraintLocator *locator);
2812+
};
2813+
27942814
} // end namespace constraints
27952815
} // end namespace swift
27962816

include/swift/Sema/Constraint.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ enum class ConversionRestrictionKind {
269269
/// Implicit conversion from a value of CGFloat type to a value of Double type
270270
/// via an implicit Double initializer call passing a CGFloat value.
271271
CGFloatToDouble,
272+
/// Implicit conversion between Swift and C pointers:
273+
// - Unsafe[Mutable]RawPointer -> Unsafe[Mutable]Pointer<[U]Int>
274+
// - Unsafe[Mutable]Pointer<Int{8, 16, ...}> <-> Unsafe[Mutable]Pointer<UInt{8, 16, ...}>
275+
PointerToCPointer,
272276
};
273277

274278
/// Specifies whether a given conversion requires the creation of a temporary

include/swift/Sema/ConstraintSystem.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4859,6 +4859,12 @@ class ConstraintSystem {
48594859
TypeMatchOptions flags,
48604860
ConstraintLocatorBuilder locator);
48614861

4862+
/// Simplify a conversion between Swift and C pointers.
4863+
SolutionKind
4864+
simplifyPointerToCPointerRestriction(Type type1, Type type2,
4865+
TypeMatchOptions flags,
4866+
ConstraintLocatorBuilder locator);
4867+
48624868
public:
48634869
/// Simplify the system of constraints, by breaking down complex
48644870
/// constraints into simpler constraints.
@@ -5205,6 +5211,10 @@ class ConstraintSystem {
52055211
/// as the anchor, and a path to the argument at index `0`.
52065212
ConstraintLocator *getArgumentLocator(Expr *expr);
52075213

5214+
/// Determine whether given locator represents an argument to declaration
5215+
/// imported from C/ObjectiveC.
5216+
bool isArgumentOfImportedDecl(ConstraintLocatorBuilder locator);
5217+
52085218
SWIFT_DEBUG_DUMP;
52095219
SWIFT_DEBUG_DUMPER(dump(Expr *));
52105220

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6634,7 +6634,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
66346634
return result;
66356635
}
66366636

6637-
case ConversionRestrictionKind::PointerToPointer: {
6637+
case ConversionRestrictionKind::PointerToPointer:
6638+
case ConversionRestrictionKind::PointerToCPointer: {
66386639
TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
66396640
Type unwrappedToTy = toType->getOptionalObjectType();
66406641

lib/Sema/CSDiagnostics.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6691,6 +6691,7 @@ void NonEphemeralConversionFailure::emitSuggestionNotes() const {
66916691
case ConversionRestrictionKind::ExistentialMetatypeToAnyObject:
66926692
case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass:
66936693
case ConversionRestrictionKind::PointerToPointer:
6694+
case ConversionRestrictionKind::PointerToCPointer:
66946695
case ConversionRestrictionKind::ArrayUpcast:
66956696
case ConversionRestrictionKind::DictionaryUpcast:
66966697
case ConversionRestrictionKind::SetUpcast:
@@ -7824,3 +7825,17 @@ bool InvalidWeakAttributeUse::diagnoseAsError() {
78247825

78257826
return true;
78267827
}
7828+
7829+
bool SwiftToCPointerConversionInInvalidContext::diagnoseAsError() {
7830+
auto argInfo = getFunctionArgApplyInfo(getLocator());
7831+
if (!argInfo)
7832+
return false;
7833+
7834+
auto *callee = argInfo->getCallee();
7835+
auto argType = resolveType(argInfo->getArgType());
7836+
auto paramType = resolveType(argInfo->getParamType());
7837+
7838+
emitDiagnostic(diag::cannot_convert_argument_value_for_swift_func, argType,
7839+
paramType, callee->getDescriptiveKind(), callee->getName());
7840+
return true;
7841+
}

lib/Sema/CSDiagnostics.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2607,6 +2607,26 @@ class InvalidWeakAttributeUse final : public FailureDiagnostic {
26072607
bool diagnoseAsError() override;
26082608
};
26092609

2610+
/// Diagnose situations where Swift -> C pointer implicit conversion
2611+
/// is attempted on a Swift function instead of one imported from C header.
2612+
///
2613+
/// \code
2614+
/// func test(_: UnsafePointer<UInt8>) {}
2615+
///
2616+
/// func pass_ptr(ptr: UnsafeRawPointer) {
2617+
/// test(ptr) // Only okay if `test` was an imported C function.
2618+
/// }
2619+
/// \endcode
2620+
class SwiftToCPointerConversionInInvalidContext final
2621+
: public FailureDiagnostic {
2622+
public:
2623+
SwiftToCPointerConversionInInvalidContext(const Solution &solution,
2624+
ConstraintLocator *locator)
2625+
: FailureDiagnostic(solution, locator) {}
2626+
2627+
bool diagnoseAsError() override;
2628+
};
2629+
26102630
} // end namespace constraints
26112631
} // end namespace swift
26122632

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,3 +2000,15 @@ AllowNonOptionalWeak *AllowNonOptionalWeak::create(ConstraintSystem &cs,
20002000
ConstraintLocator *locator) {
20012001
return new (cs.getAllocator()) AllowNonOptionalWeak(cs, locator);
20022002
}
2003+
2004+
bool AllowSwiftToCPointerConversion::diagnose(const Solution &solution,
2005+
bool asNote) const {
2006+
SwiftToCPointerConversionInInvalidContext failure(solution, getLocator());
2007+
return failure.diagnose(asNote);
2008+
}
2009+
2010+
AllowSwiftToCPointerConversion *
2011+
AllowSwiftToCPointerConversion::create(ConstraintSystem &cs,
2012+
ConstraintLocator *locator) {
2013+
return new (cs.getAllocator()) AllowSwiftToCPointerConversion(cs, locator);
2014+
}

lib/Sema/CSSimplify.cpp

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5902,6 +5902,43 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
59025902
ConversionRestrictionKind::PointerToPointer);
59035903
}
59045904
}
5905+
5906+
// If both sides are non-optional pointers, let's check whether
5907+
// this argument supports Swift -> C pointer conversions.
5908+
//
5909+
// Do some light verification before recording restriction to
5910+
// avoid allocating constraints for obviously invalid cases.
5911+
if (type1IsPointer && !type1IsOptional && !type2IsOptional &&
5912+
(shouldAttemptFixes() || isArgumentOfImportedDecl(locator))) {
5913+
// UnsafeRawPointer -> UnsafePointer<[U]Int8>
5914+
if (type1PointerKind == PTK_UnsafeRawPointer &&
5915+
pointerKind == PTK_UnsafePointer) {
5916+
conversionsOrFixes.push_back(
5917+
ConversionRestrictionKind::PointerToCPointer);
5918+
}
5919+
5920+
// UnsafeMutableRawPointer -> Unsafe[Mutable]Pointer<[U]Int8>
5921+
if (type1PointerKind == PTK_UnsafeMutableRawPointer &&
5922+
(pointerKind == PTK_UnsafePointer ||
5923+
pointerKind == PTK_UnsafeMutablePointer)) {
5924+
conversionsOrFixes.push_back(
5925+
ConversionRestrictionKind::PointerToCPointer);
5926+
}
5927+
5928+
// Unsafe[Mutable]Pointer -> Unsafe[Mutable]Pointer
5929+
if (type1PointerKind == PTK_UnsafePointer &&
5930+
pointerKind == PTK_UnsafePointer) {
5931+
conversionsOrFixes.push_back(
5932+
ConversionRestrictionKind::PointerToCPointer);
5933+
}
5934+
5935+
if (type1PointerKind == PTK_UnsafeMutablePointer &&
5936+
(pointerKind == PTK_UnsafePointer ||
5937+
pointerKind == PTK_UnsafeMutablePointer)) {
5938+
conversionsOrFixes.push_back(
5939+
ConversionRestrictionKind::PointerToCPointer);
5940+
}
5941+
}
59055942
}
59065943
break;
59075944

@@ -11128,7 +11165,10 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
1112811165

1112911166
return matchPointerBaseTypes(ptr1, ptr2);
1113011167
}
11131-
11168+
11169+
case ConversionRestrictionKind::PointerToCPointer:
11170+
return simplifyPointerToCPointerRestriction(type1, type2, flags, locator);
11171+
1113211172
// T < U or T is bridged to V where V < U ===> Array<T> <c Array<U>
1113311173
case ConversionRestrictionKind::ArrayUpcast: {
1113411174
Type baseType1 = *isArrayType(type1);
@@ -11355,6 +11395,122 @@ ConstraintSystem::simplifyRestrictedConstraint(
1135511395
llvm_unreachable("Unhandled SolutionKind in switch.");
1135611396
}
1135711397

11398+
ConstraintSystem::SolutionKind
11399+
ConstraintSystem::simplifyPointerToCPointerRestriction(
11400+
Type type1, Type type2, TypeMatchOptions flags,
11401+
ConstraintLocatorBuilder locator) {
11402+
bool inCorrectPosition = isArgumentOfImportedDecl(locator);
11403+
11404+
if (inCorrectPosition) {
11405+
// Make sure that solutions with implicit pointer conversions
11406+
// are always worse than the ones without them.
11407+
increaseScore(SK_ImplicitValueConversion);
11408+
} else {
11409+
// If this is not an imported function, let's not proceed with
11410+
// the conversion, unless in diagnostic mode.
11411+
if (!shouldAttemptFixes())
11412+
return SolutionKind::Error;
11413+
11414+
// Let's attempt to convert the types and record a tailored
11415+
// fix if that succeeds.
11416+
}
11417+
11418+
auto &ctx = getASTContext();
11419+
11420+
PointerTypeKind swiftPtrKind, cPtrKind;
11421+
11422+
auto swiftPtr = type1->getAnyPointerElementType(swiftPtrKind);
11423+
auto cPtr = type2->getAnyPointerElementType(cPtrKind);
11424+
11425+
assert(swiftPtr);
11426+
assert(cPtr);
11427+
11428+
auto markSupported = [&]() -> SolutionKind {
11429+
if (inCorrectPosition)
11430+
return SolutionKind::Solved;
11431+
11432+
// If conversion cannot be allowed on account of declaration,
11433+
// let's add a tailored fix.
11434+
auto *fix = AllowSwiftToCPointerConversion::create(
11435+
*this, getConstraintLocator(locator));
11436+
11437+
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
11438+
};
11439+
11440+
// Unsafe[Mutable]RawPointer -> Unsafe[Mutable]Pointer<[U]Int8>
11441+
if (swiftPtrKind == PTK_UnsafeRawPointer ||
11442+
swiftPtrKind == PTK_UnsafeMutableRawPointer) {
11443+
// Since it's a C pointer on parameter side it would always
11444+
// be fully resolved.
11445+
if (cPtr->isInt8() || cPtr->isUInt8())
11446+
return markSupported();
11447+
} else {
11448+
// Unsafe[Mutable]Pointer<T> -> Unsafe[Mutable]Pointer<[U]Int8>
11449+
if (cPtr->isInt8() || cPtr->isUInt8()) {
11450+
// <T> can default to the type of C pointer.
11451+
addConstraint(
11452+
ConstraintKind::Defaultable, swiftPtr, cPtr,
11453+
locator.withPathElement(LocatorPathElt::GenericArgument(0)));
11454+
return markSupported();
11455+
}
11456+
11457+
auto elementLoc =
11458+
locator.withPathElement(LocatorPathElt::GenericArgument(0));
11459+
11460+
// Unsafe[Mutable]Pointer<Int{8, 16, ...}> <->
11461+
// Unsafe[Mutable]Pointer<UInt{8, 16, ...}>
11462+
11463+
if (swiftPtr->isInt() || swiftPtr->isUInt()) {
11464+
addConstraint(ConstraintKind::Equal, cPtr,
11465+
swiftPtr->isUInt() ? ctx.getIntType() : ctx.getUIntType(),
11466+
elementLoc);
11467+
return markSupported();
11468+
}
11469+
11470+
if (swiftPtr->isInt8() || swiftPtr->isUInt8()) {
11471+
addConstraint(ConstraintKind::Equal, cPtr,
11472+
swiftPtr->isUInt8() ? ctx.getInt8Type()
11473+
: ctx.getUInt8Type(),
11474+
elementLoc);
11475+
return markSupported();
11476+
}
11477+
11478+
if (swiftPtr->isInt16() || swiftPtr->isUInt16()) {
11479+
addConstraint(ConstraintKind::Equal, cPtr,
11480+
swiftPtr->isUInt16() ? ctx.getInt16Type()
11481+
: ctx.getUInt16Type(),
11482+
elementLoc);
11483+
return markSupported();
11484+
}
11485+
11486+
if (swiftPtr->isInt32() || swiftPtr->isUInt32()) {
11487+
addConstraint(ConstraintKind::Equal, cPtr,
11488+
swiftPtr->isUInt32() ? ctx.getInt32Type()
11489+
: ctx.getUInt32Type(),
11490+
elementLoc);
11491+
return markSupported();
11492+
}
11493+
11494+
if (swiftPtr->isInt64() || swiftPtr->isUInt64()) {
11495+
addConstraint(ConstraintKind::Equal, cPtr,
11496+
swiftPtr->isUInt64() ? ctx.getInt64Type()
11497+
: ctx.getUInt64Type(),
11498+
elementLoc);
11499+
return markSupported();
11500+
}
11501+
}
11502+
11503+
// If the conversion is unsupported, let's record a generic argument mismatch.
11504+
if (shouldAttemptFixes() && !inCorrectPosition) {
11505+
auto *fix = AllowArgumentMismatch::create(*this, type1, type2,
11506+
getConstraintLocator(locator));
11507+
return recordFix(fix, /*impact=*/2) ? SolutionKind::Error
11508+
: SolutionKind::Solved;
11509+
}
11510+
11511+
return SolutionKind::Error;
11512+
}
11513+
1135811514
static bool isAugmentingFix(ConstraintFix *fix) {
1135911515
switch (fix->getKind()) {
1136011516
case FixKind::TreatRValueAsLValue:
@@ -11816,6 +11972,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1181611972
case FixKind::AddSendableAttribute:
1181711973
case FixKind::DropThrowsAttribute:
1181811974
case FixKind::DropAsyncAttribute:
11975+
case FixKind::AllowSwiftToCPointerConversion:
1181911976
llvm_unreachable("handled elsewhere");
1182011977
}
1182111978

lib/Sema/Constraint.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,8 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) {
546546
return "[inout-to-pointer]";
547547
case ConversionRestrictionKind::PointerToPointer:
548548
return "[pointer-to-pointer]";
549+
case ConversionRestrictionKind::PointerToCPointer:
550+
return "[pointer-to-c-pointer]";
549551
case ConversionRestrictionKind::ArrayUpcast:
550552
return "[array-upcast]";
551553
case ConversionRestrictionKind::DictionaryUpcast:

0 commit comments

Comments
 (0)