Skip to content

Commit ecc6bd5

Browse files
authored
Type system support for raw pointer conversion. (#3685)
* [Type System] Handle raw pointer conversion. As proposed in SE-0107: UnsafeRawPointer. https://github.com/apple/swift-evolution/blob/master/proposals/0107-unsaferawpointer.md#implicit-argument-conversion UnsafeMutablePointer<T> -> UnsafeMutableRawPointer UnsafeMutablePointer<T> -> UnsafeRawPointer UnsafePointer<T> -> UnsafeRawPointer UnsafeMutableRawPointer -> UnsafeRawPointer inout: &anyVar -> UnsafeMutableRawPointer &anyVar -> UnsafeRawPointer array -> UnsafeRawPointer string -> UnsafeRawPointer varArray -> UnsafeMutableRawPointer * Rename expectEqual(_, _, sameValue:) to expectEqualTest to workaround a type system bug. <rdar://26058520> Generic type constraints incorrectly applied to functions with the same name This is exposed by additions to the type system for UnsafeRawPointer. Warning: unit tests fail very confusingly without this fix.
1 parent 765ddb0 commit ecc6bd5

File tree

16 files changed

+280
-49
lines changed

16 files changed

+280
-49
lines changed

include/swift/AST/ASTContext.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,13 @@ class ASTContext {
424424

425425
/// Retrieve the declaration of Swift.COpaquePointer.
426426
NominalTypeDecl *getOpaquePointerDecl() const;
427-
427+
428+
/// Retrieve the declaration of Swift.UnsafeMutableRawPointer.
429+
NominalTypeDecl *getUnsafeMutableRawPointerDecl() const;
430+
431+
/// Retrieve the declaration of Swift.UnsafeRawPointer.
432+
NominalTypeDecl *getUnsafeRawPointerDecl() const;
433+
428434
/// Retrieve the declaration of Swift.UnsafeMutablePointer<T>.
429435
NominalTypeDecl *getUnsafeMutablePointerDecl() const;
430436

include/swift/AST/Decl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2712,6 +2712,8 @@ enum { NumOptionalTypeKinds = 2 };
27122712

27132713
// Kinds of pointer types.
27142714
enum PointerTypeKind : unsigned {
2715+
PTK_UnsafeMutableRawPointer,
2716+
PTK_UnsafeRawPointer,
27152717
PTK_UnsafeMutablePointer,
27162718
PTK_UnsafePointer,
27172719
PTK_AutoreleasingUnsafeMutablePointer,

lib/AST/ASTContext.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ struct ASTContext::Implementation {
137137
/// The declaration of Swift.ImplicitlyUnwrappedOptional<T>.None.
138138
EnumElementDecl *ImplicitlyUnwrappedOptionalNoneDecl = nullptr;
139139

140+
/// The declaration of Swift.UnsafeMutableRawPointer.
141+
NominalTypeDecl *UnsafeMutableRawPointerDecl = nullptr;
142+
VarDecl *UnsafeMutableRawPointerMemoryDecl = nullptr;
143+
144+
/// The declaration of Swift.UnsafeRawPointer.
145+
NominalTypeDecl *UnsafeRawPointerDecl = nullptr;
146+
VarDecl *UnsafeRawPointerMemoryDecl = nullptr;
147+
140148
/// The declaration of Swift.UnsafeMutablePointer<T>.
141149
NominalTypeDecl *UnsafeMutablePointerDecl = nullptr;
142150
VarDecl *UnsafeMutablePointerMemoryDecl = nullptr;
@@ -702,11 +710,27 @@ NominalTypeDecl *ASTContext::getOptionSetDecl() const {
702710
return Impl.OptionSetDecl;
703711
}
704712

713+
NominalTypeDecl *ASTContext::getUnsafeMutableRawPointerDecl() const {
714+
if (!Impl.UnsafeMutableRawPointerDecl)
715+
Impl.UnsafeMutableRawPointerDecl = findStdlibType(
716+
*this, "UnsafeMutableRawPointer", 0);
717+
718+
return Impl.UnsafeMutableRawPointerDecl;
719+
}
720+
721+
NominalTypeDecl *ASTContext::getUnsafeRawPointerDecl() const {
722+
if (!Impl.UnsafeRawPointerDecl)
723+
Impl.UnsafeRawPointerDecl = findStdlibType(
724+
*this, "UnsafeRawPointer", 0);
725+
726+
return Impl.UnsafeRawPointerDecl;
727+
}
728+
705729
NominalTypeDecl *ASTContext::getUnsafeMutablePointerDecl() const {
706730
if (!Impl.UnsafeMutablePointerDecl)
707731
Impl.UnsafeMutablePointerDecl = findStdlibType(
708732
*this, "UnsafeMutablePointer", 1);
709-
733+
710734
return Impl.UnsafeMutablePointerDecl;
711735
}
712736

@@ -771,6 +795,14 @@ static VarDecl *getPointeeProperty(VarDecl *&cache,
771795
VarDecl *
772796
ASTContext::getPointerPointeePropertyDecl(PointerTypeKind ptrKind) const {
773797
switch (ptrKind) {
798+
case PTK_UnsafeMutableRawPointer:
799+
return getPointeeProperty(Impl.UnsafeMutableRawPointerMemoryDecl,
800+
&ASTContext::getUnsafeMutableRawPointerDecl,
801+
*this);
802+
case PTK_UnsafeRawPointer:
803+
return getPointeeProperty(Impl.UnsafeRawPointerMemoryDecl,
804+
&ASTContext::getUnsafeRawPointerDecl,
805+
*this);
774806
case PTK_UnsafeMutablePointer:
775807
return getPointeeProperty(Impl.UnsafeMutablePointerMemoryDecl,
776808
&ASTContext::getUnsafeMutablePointerDecl,
@@ -1100,7 +1132,9 @@ bool ASTContext::hasOptionalIntrinsics(LazyResolver *resolver) const {
11001132
}
11011133

11021134
bool ASTContext::hasPointerArgumentIntrinsics(LazyResolver *resolver) const {
1103-
return getUnsafeMutablePointerDecl()
1135+
return getUnsafeMutableRawPointerDecl()
1136+
&& getUnsafeRawPointerDecl()
1137+
&& getUnsafeMutablePointerDecl()
11041138
&& getUnsafePointerDecl()
11051139
&& (!LangOpts.EnableObjCInterop || getAutoreleasingUnsafeMutablePointerDecl())
11061140
&& getConvertPointerToPointerArgument(resolver)

lib/AST/ASTVerifier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,7 @@ struct ASTNodeBase {};
11621162
Out << "\n";
11631163
abort();
11641164
}
1165-
if (PTK != PTK_UnsafePointer) {
1165+
if (PTK != PTK_UnsafePointer && PTK != PTK_UnsafeRawPointer) {
11661166
Out << "StringToPointer converts to non-const pointer:\n";
11671167
E->print(Out);
11681168
Out << "\n";

lib/AST/Type.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,8 +617,18 @@ CanType CanType::getAnyOptionalObjectTypeImpl(CanType type,
617617
}
618618

619619
Type TypeBase::getAnyPointerElementType(PointerTypeKind &PTK) {
620+
auto &C = getASTContext();
621+
if (auto nominalTy = getAs<NominalType>()) {
622+
if (nominalTy->getDecl() == C.getUnsafeMutableRawPointerDecl()) {
623+
PTK = PTK_UnsafeMutableRawPointer;
624+
return C.TheEmptyTupleType;
625+
}
626+
if (nominalTy->getDecl() == C.getUnsafeRawPointerDecl()) {
627+
PTK = PTK_UnsafeRawPointer;
628+
return C.TheEmptyTupleType;
629+
}
630+
}
620631
if (auto boundTy = getAs<BoundGenericType>()) {
621-
auto &C = getASTContext();
622632
if (boundTy->getDecl() == C.getUnsafeMutablePointerDecl()) {
623633
PTK = PTK_UnsafeMutablePointer;
624634
} else if (boundTy->getDecl() == C.getUnsafePointerDecl()) {
@@ -2214,6 +2224,8 @@ getForeignRepresentable(Type type, ForeignLanguage language,
22142224
PointerTypeKind pointerKind;
22152225
if (auto pointerElt = type->getAnyPointerElementType(pointerKind)) {
22162226
switch (pointerKind) {
2227+
case PTK_UnsafeMutableRawPointer:
2228+
case PTK_UnsafeRawPointer:
22172229
case PTK_UnsafeMutablePointer:
22182230
case PTK_UnsafePointer:
22192231
// An UnsafeMutablePointer<T> or UnsafePointer<T> is

lib/IRGen/GenClangType.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ clang::CanQualType GenClangType::visitStructType(CanStructType type) {
213213
ctx.VoidPtrTy);
214214
CHECK_NAMED_TYPE("ObjCBool", ctx.ObjCBuiltinBoolTy);
215215
CHECK_NAMED_TYPE("Selector", getClangSelectorType(ctx));
216+
CHECK_NAMED_TYPE("UnsafeRawPointer", ctx.VoidPtrTy);
217+
CHECK_NAMED_TYPE("UnsafeMutableRawPointer", ctx.VoidPtrTy);
216218
#undef CHECK_NAMED_TYPE
217219

218220
// Map vector types to the corresponding C vectors.

lib/SILGen/SILGenExpr.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3299,7 +3299,7 @@ RValue RValueEmitter::visitInOutToPointerExpr(InOutToPointerExpr *E,
32993299
(void)elt;
33003300

33013301
AccessKind accessKind =
3302-
(pointerKind == PTK_UnsafePointer
3302+
((pointerKind == PTK_UnsafePointer || pointerKind == PTK_UnsafeRawPointer)
33033303
? AccessKind::Read : AccessKind::ReadWrite);
33043304

33053305
// Get the original lvalue.
@@ -3327,10 +3327,11 @@ ManagedValue SILGenFunction::emitLValueToPointer(SILLocation loc,
33273327
!= loweredTy.getSwiftRValueType()) {
33283328
lv.addSubstToOrigComponent(opaqueTy, loweredTy);
33293329
}
3330-
33313330
switch (pointerKind) {
33323331
case PTK_UnsafeMutablePointer:
33333332
case PTK_UnsafePointer:
3333+
case PTK_UnsafeMutableRawPointer:
3334+
case PTK_UnsafeRawPointer:
33343335
// +1 is fine.
33353336
break;
33363337

lib/Sema/CSDiag.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3692,7 +3692,9 @@ bool FailureDiagnosis::diagnoseContextualConversionError() {
36923692
}
36933693
}
36943694
} else if (contextDecl == CS->TC.Context.getUnsafePointerDecl() ||
3695-
contextDecl == CS->TC.Context.getUnsafeMutablePointerDecl()) {
3695+
contextDecl == CS->TC.Context.getUnsafeMutablePointerDecl() ||
3696+
contextDecl == CS->TC.Context.getUnsafeRawPointerDecl() ||
3697+
contextDecl == CS->TC.Context.getUnsafeMutableRawPointerDecl()) {
36963698
SmallVector<Type, 4> scratch;
36973699
for (Type arg : contextualType->getAllGenericArgs(scratch)) {
36983700
if (arg->isEqual(exprType) && expr->getType()->isLValueType()) {
@@ -5080,7 +5082,7 @@ bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) {
50805082
unwrappedType->getAnyPointerElementType(pointerKind)) {
50815083

50825084
// If the element type is Void, then we allow any input type, since
5083-
// everything is convertible to UnsafePointer<Void>
5085+
// everything is convertible to UnsafeRawPointer
50845086
if (pointerEltType->isVoid())
50855087
contextualType = Type();
50865088
else
@@ -5094,7 +5096,7 @@ bool FailureDiagnosis::visitInOutExpr(InOutExpr *IOE) {
50945096
// it is mutable.
50955097
if (pointerKind == PTK_UnsafeMutablePointer) {
50965098
contextualType = ArraySliceType::get(contextualType);
5097-
} else if (pointerKind == PTK_UnsafePointer) {
5099+
} else if (pointerKind == PTK_UnsafePointer || pointerKind == PTK_UnsafeRawPointer) {
50985100
// If we're converting to an UnsafePointer, then the programmer
50995101
// specified an & unnecessarily. Produce a fixit hint to remove it.
51005102
diagnose(IOE->getLoc(), diag::extra_address_of_unsafepointer,

lib/Sema/CSSimplify.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
17251725
if (Type pointeeTy =
17261726
unwrappedType2->getAnyPointerElementType(pointerKind)) {
17271727
switch (pointerKind) {
1728+
case PTK_UnsafeRawPointer:
1729+
case PTK_UnsafeMutableRawPointer:
17281730
case PTK_UnsafePointer:
17291731
case PTK_UnsafeMutablePointer:
17301732
// UnsafeMutablePointer can be converted from an inout reference to a
@@ -1775,20 +1777,29 @@ ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
17751777
unwrappedType1->getAnyPointerElementType(type1PointerKind)};
17761778
bool optionalityMatches =
17771779
type1OptionalKind == OTK_None || type2OptionalKind != OTK_None;
1778-
if (type1IsPointer && optionalityMatches &&
1779-
type1PointerKind == PTK_UnsafeMutablePointer) {
1780-
// Favor an UnsafeMutablePointer-to-UnsafeMutablePointer
1781-
// conversion.
1782-
if (type1PointerKind != pointerKind)
1783-
increaseScore(ScoreKind::SK_ScalarPointerConversion);
1784-
conversionsOrFixes.push_back(
1785-
ConversionRestrictionKind::PointerToPointer);
1780+
if (type1IsPointer && optionalityMatches) {
1781+
if (type1PointerKind == PTK_UnsafeMutablePointer) {
1782+
// Favor an UnsafeMutablePointer-to-UnsafeMutablePointer
1783+
// conversion.
1784+
if (type1PointerKind != pointerKind)
1785+
increaseScore(ScoreKind::SK_ScalarPointerConversion);
1786+
conversionsOrFixes.push_back(
1787+
ConversionRestrictionKind::PointerToPointer);
1788+
}
1789+
// UnsafeMutableRawPointer -> UnsafeRawPointer
1790+
else if (type1PointerKind == PTK_UnsafeMutableRawPointer &&
1791+
pointerKind == PTK_UnsafeRawPointer) {
1792+
if (type1PointerKind != pointerKind)
1793+
increaseScore(ScoreKind::SK_ScalarPointerConversion);
1794+
conversionsOrFixes.push_back(
1795+
ConversionRestrictionKind::PointerToPointer);
1796+
}
17861797
}
1787-
1788-
// UnsafePointer can also be converted from an array
1789-
// or string value, or a UnsafePointer or
1798+
// UnsafePointer and UnsafeRawPointer can also be converted from an
1799+
// array or string value, or a UnsafePointer or
17901800
// AutoreleasingUnsafeMutablePointer.
1791-
if (pointerKind == PTK_UnsafePointer) {
1801+
if (pointerKind == PTK_UnsafePointer
1802+
|| pointerKind == PTK_UnsafeRawPointer) {
17921803
if (isArrayType(type1)) {
17931804
conversionsOrFixes.push_back(
17941805
ConversionRestrictionKind::ArrayToPointer);

stdlib/private/StdlibCollectionUnittest/CheckCollectionInstance.swift.gyb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ public func check${Traversal}Collection<
388388
successor3 = succ(successor3)
389389
for s in [ successor1, successor2, successor3 ] {
390390
expectEqual(allIndices[i + 1], s, ${trace})
391-
expectEqual(
391+
expectEqualTest(
392392
expectedArray[i + 1], collection[s], ${trace}, sameValue: sameValue)
393393
}
394394
}
@@ -403,14 +403,14 @@ public func check${Traversal}Collection<
403403
predecessor3 = pred(predecessor3)
404404
for p in [ predecessor1, predecessor2, predecessor3 ] {
405405
expectEqual(allIndices[i - 1], p, ${trace})
406-
expectEqual(
406+
expectEqualTest(
407407
expectedArray[i - 1], collection[p], ${trace}, sameValue: sameValue)
408408
}
409409
}
410410
for i in 1..<allIndices.count {
411411
let index = succ(pred(allIndices[i]))
412412
expectEqual(allIndices[i], index, ${trace})
413-
expectEqual(
413+
expectEqualTest(
414414
expectedArray[i], collection[index], ${trace}, sameValue: sameValue)
415415
}
416416

stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,20 +134,20 @@ public func identityComp(_ element: MinimalComparableValue)
134134
}
135135

136136
public func expectEqual<T : Equatable>(_ expected: T, _ actual: T, ${TRACE}) {
137-
expectEqual(expected, actual, ${trace}, showFrame: false) {$0 == $1}
137+
expectEqualTest(expected, actual, ${trace}, showFrame: false) {$0 == $1}
138138
}
139139

140140
public func expectEqual<T : Equatable, U : Equatable>(
141141
_ expected: (T, U), _ actual: (T, U), ${TRACE}) {
142-
expectEqual(expected.0, actual.0, ${trace}, showFrame: false) {$0 == $1}
143-
expectEqual(expected.1, actual.1, ${trace}, showFrame: false) {$0 == $1}
142+
expectEqualTest(expected.0, actual.0, ${trace}, showFrame: false) {$0 == $1}
143+
expectEqualTest(expected.1, actual.1, ${trace}, showFrame: false) {$0 == $1}
144144
}
145145

146146
public func expectEqual<T : Equatable, U : Equatable, V : Equatable>(
147147
_ expected: (T, U, V), _ actual: (T, U, V), ${TRACE}) {
148-
expectEqual(expected.0, actual.0, ${trace}, showFrame: false) {$0 == $1}
149-
expectEqual(expected.1, actual.1, ${trace}, showFrame: false) {$0 == $1}
150-
expectEqual(expected.2, actual.2, ${trace}, showFrame: false) {$0 == $1}
148+
expectEqualTest(expected.0, actual.0, ${trace}, showFrame: false) {$0 == $1}
149+
expectEqualTest(expected.1, actual.1, ${trace}, showFrame: false) {$0 == $1}
150+
expectEqualTest(expected.2, actual.2, ${trace}, showFrame: false) {$0 == $1}
151151
}
152152

153153
public func expectationFailure(
@@ -160,7 +160,10 @@ public func expectationFailure(
160160
print(message, terminator: message == "" ? "" : "\n")
161161
}
162162

163-
public func expectEqual<T>(
163+
// Renamed to avoid collision with expectEqual(_, _, TRACE).
164+
// See <rdar://26058520> Generic type constraints incorrectly applied to
165+
// functions with the same name
166+
public func expectEqualTest<T>(
164167
_ expected: T, _ actual: T, ${TRACE}, sameValue equal: (T, T) -> Bool
165168
) {
166169
if !equal(expected, actual) {
@@ -230,7 +233,7 @@ public func expectNotEqual<T : Equatable>(
230233
public func expectEqual${Generic}(
231234
_ expected: ${EquatableType}, _ actual: ${EquatableType}, ${TRACE}
232235
) {
233-
expectEqual(expected, actual, ${trace}, showFrame: false) { $0 == $1 }
236+
expectEqualTest(expected, actual, ${trace}, showFrame: false) { $0 == $1 }
234237
}
235238

236239
public func expectOptionalEqual${Generic}(

test/1_stdlib/simd.swift.gyb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,24 @@ simdTestSuite.test("vector init") {
9999

100100
// Check empty initializer.
101101
var ${vec} = ${vectype}()
102-
expectEqual(${[0]*size}, ${vec}, sameValue: same)
102+
expectEqualTest(${[0]*size}, ${vec}, sameValue: same)
103103

104104
// Check literal initializer.
105105
${vec} = ${vectype}(2)
106-
expectEqual(${[2]*size}, ${vec}, sameValue: same)
106+
expectEqualTest(${[2]*size}, ${vec}, sameValue: same)
107107

108108
// Check scalar initializer.
109-
expectEqual(${vectype}(${type}(2)), ${vec}, sameValue: same)
109+
expectEqualTest(${vectype}(${type}(2)), ${vec}, sameValue: same)
110110

111111
// Check elementwise initializer.
112112
${vec} = ${vectype}(${', '.join(map(lambda i: str(i), range(size)))})
113-
expectEqual(${range(size)}, ${vec}, sameValue: same)
113+
expectEqualTest(${range(size)}, ${vec}, sameValue: same)
114114

115115
// Check labeled elementwise initializer.
116116
${vec} = ${vectype}(${', '.join(map(lambda i:
117117
component[i][1:] + ':' + str(i),
118118
range(size)))})
119-
expectEqual(${range(size)}, ${vec}, sameValue: same)
119+
expectEqualTest(${range(size)}, ${vec}, sameValue: same)
120120

121121
// Previous checks implicitly use the array initializer, so we're good
122122
// with that one already.
@@ -141,7 +141,7 @@ simdTestSuite.test("vector elements") {
141141

142142
// Check getting and setting [i]
143143
for i in 0..<${size} { ${vec}[i] = ${vec}[i]/2 }
144-
expectEqual(${vec}, ${range(size)}, sameValue: same)
144+
expectEqualTest(${vec}, ${range(size)}, sameValue: same)
145145

146146
% end # for size in [2,3,4]
147147
% end # for type in scalar_types
@@ -245,7 +245,7 @@ simdTestSuite.test("matrix init") {
245245

246246
// Check empty initializer.
247247
var ${mat} = ${mattype}()
248-
expectEqual(${mat}, ${mattype}(${[[0]*rows]*cols}), sameValue: same)
248+
expectEqualTest(${mat}, ${mattype}(${[[0]*rows]*cols}), sameValue: same)
249249

250250
// Check scalar initializer.
251251
${mat} = ${mattype}(2)
@@ -280,7 +280,7 @@ simdTestSuite.test("matrix init") {
280280
}
281281

282282
// Round-trip through C matrix type.
283-
expectEqual(${mat}, ${mattype}(${mat}.cmatrix), sameValue: same)
283+
expectEqualTest(${mat}, ${mattype}(${mat}.cmatrix), sameValue: same)
284284

285285
% end # for rows
286286
% end # for cols
@@ -311,10 +311,10 @@ simdTestSuite.test("matrix elements") {
311311
for i in 0..<${cols} {
312312
${mat}[i] = ${type}(i+1)*${basis}[i]
313313
}
314-
expectEqual(
314+
expectEqualTest(
315315
${mat}, ${mattype}(diagonal:${range(1, diagsize + 1)}), sameValue: same)
316316
for i in 0..<${cols} {
317-
expectEqual(${mat}[i], ${type}(i + 1)*${basis}[i], sameValue: same)
317+
expectEqualTest(${mat}[i], ${type}(i + 1)*${basis}[i], sameValue: same)
318318
}
319319

320320
// Check getting and setting elements (and transpose).

0 commit comments

Comments
 (0)