Skip to content

Commit 6e6a57b

Browse files
authored
Merge pull request #82145 from hnrklssn/swiftify-sizedby-6.2
Cherry-pick "[Swiftify] Support __sized_by on byte-sized pointee types" to release/6.2
2 parents dbd4325 + 993929c commit 6e6a57b

File tree

14 files changed

+340
-74
lines changed

14 files changed

+340
-74
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9119,16 +9119,39 @@ namespace {
91199119
};
91209120
} // namespace
91219121

9122-
static bool SwiftifiableCAT(const clang::CountAttributedType *CAT) {
9123-
return CAT && CATExprValidator().Visit(CAT->getCountExpr());
9122+
// Don't try to transform any Swift types that _SwiftifyImport doesn't know how
9123+
// to handle.
9124+
static bool SwiftifiableCountedByPointerType(Type swiftType) {
9125+
Type nonnullType = swiftType->lookThroughSingleOptionalType();
9126+
PointerTypeKind PTK;
9127+
return nonnullType->getAnyPointerElementType(PTK) &&
9128+
(PTK == PTK_UnsafePointer || PTK == PTK_UnsafeMutablePointer);
91249129
}
9125-
9126-
static bool SwiftifiablePointerType(Type swiftType) {
9127-
// don't try to transform any Swift types that _SwiftifyImport doesn't know how to handle
9130+
static bool SwiftifiableSizedByPointerType(const clang::ASTContext &ctx,
9131+
Type swiftType,
9132+
const clang::CountAttributedType *CAT) {
91289133
Type nonnullType = swiftType->lookThroughSingleOptionalType();
9134+
if (nonnullType->isOpaquePointer())
9135+
return true;
91299136
PointerTypeKind PTK;
9130-
return nonnullType->isOpaquePointer() ||
9131-
(nonnullType->getAnyPointerElementType(PTK) && PTK != PTK_AutoreleasingUnsafeMutablePointer);
9137+
if (!nonnullType->getAnyPointerElementType(PTK))
9138+
return false;
9139+
if (PTK == PTK_UnsafeRawPointer || PTK == PTK_UnsafeMutableRawPointer)
9140+
return true;
9141+
if (PTK != PTK_UnsafePointer && PTK != PTK_UnsafeMutablePointer)
9142+
return false;
9143+
// We have a pointer to a type with a size. Verify that it is char-sized.
9144+
auto PtrT = CAT->getAs<clang::PointerType>();
9145+
auto PointeeT = PtrT->getPointeeType();
9146+
return ctx.getTypeSizeInChars(PointeeT).isOne();
9147+
}
9148+
static bool SwiftifiableCAT(const clang::ASTContext &ctx,
9149+
const clang::CountAttributedType *CAT,
9150+
Type swiftType) {
9151+
return CAT && CATExprValidator().Visit(CAT->getCountExpr()) &&
9152+
(CAT->isCountInBytes() ?
9153+
SwiftifiableSizedByPointerType(ctx, swiftType, CAT)
9154+
: SwiftifiableCountedByPointerType(swiftType));
91329155
}
91339156

91349157
void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
@@ -9170,7 +9193,7 @@ void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
91709193
bool returnIsStdSpan = registerStdSpanTypeMapping(
91719194
swiftReturnTy, ClangDecl->getReturnType());
91729195
auto *CAT = ClangDecl->getReturnType()->getAs<clang::CountAttributedType>();
9173-
if (SwiftifiableCAT(CAT) && SwiftifiablePointerType(swiftReturnTy)) {
9196+
if (SwiftifiableCAT(getClangASTContext(), CAT, swiftReturnTy)) {
91749197
printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX);
91759198
attachMacro = true;
91769199
}
@@ -9187,7 +9210,7 @@ void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
91879210
Type swiftParamTy = swiftParam->getInterfaceType();
91889211
bool paramHasBoundsInfo = false;
91899212
auto *CAT = clangParamTy->getAs<clang::CountAttributedType>();
9190-
if (SwiftifiableCAT(CAT) && SwiftifiablePointerType(swiftParamTy)) {
9213+
if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) {
91919214
printer.printCountedBy(CAT, index);
91929215
attachMacro = paramHasBoundsInfo = true;
91939216
}

lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -329,10 +329,7 @@ func transformType(
329329
let text = name.text
330330
let isRaw = isRawPointerType(text: text)
331331
if isRaw && !isSizedBy {
332-
throw DiagnosticError("raw pointers only supported for SizedBy", node: name)
333-
}
334-
if !isRaw && isSizedBy {
335-
throw DiagnosticError("SizedBy only supported for raw pointers", node: name)
332+
throw DiagnosticError("void pointers not supported for countedBy", node: name)
336333
}
337334

338335
guard let kind: Mutability = getPointerMutability(text: text) else {
@@ -375,6 +372,33 @@ func isMutablePointerType(_ type: TypeSyntax) -> Bool {
375372
}
376373
}
377374

375+
func getPointeeType(_ type: TypeSyntax) -> TypeSyntax? {
376+
if let optType = type.as(OptionalTypeSyntax.self) {
377+
return getPointeeType(optType.wrappedType)
378+
}
379+
if let impOptType = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) {
380+
return getPointeeType(impOptType.wrappedType)
381+
}
382+
if let attrType = type.as(AttributedTypeSyntax.self) {
383+
return getPointeeType(attrType.baseType)
384+
}
385+
386+
guard let idType = type.as(IdentifierTypeSyntax.self) else {
387+
return nil
388+
}
389+
let text = idType.name.text
390+
if text != "UnsafePointer" && text != "UnsafeMutablePointer" {
391+
return nil
392+
}
393+
guard let x = idType.genericArgumentClause else {
394+
return nil
395+
}
396+
guard let y = x.arguments.first else {
397+
return nil
398+
}
399+
return y.argument.as(TypeSyntax.self)
400+
}
401+
378402
protocol BoundsCheckedThunkBuilder {
379403
func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax]) throws -> ExprSyntax
380404
// buildBasicBoundsChecks creates a variable with the same name as the parameter it replaced,
@@ -648,6 +672,7 @@ extension PointerBoundsThunkBuilder {
648672
return try transformType(oldType, generateSpan, isSizedBy, isParameter)
649673
}
650674
}
675+
651676
var countLabel: String {
652677
return isSizedBy && generateSpan ? "byteCount" : "count"
653678
}
@@ -826,7 +851,7 @@ struct CountedOrSizedPointerThunkBuilder: ParamBoundsThunkBuilder, PointerBounds
826851
var args = argOverrides
827852
let argExpr = ExprSyntax("\(unwrappedName).baseAddress")
828853
assert(args[index] == nil)
829-
args[index] = try castPointerToOpaquePointer(unwrapIfNonnullable(argExpr))
854+
args[index] = try castPointerToTargetType(unwrapIfNonnullable(argExpr))
830855
let call = try base.buildFunctionCall(args)
831856
let ptrRef = unwrapIfNullable("\(name)")
832857

@@ -871,11 +896,16 @@ struct CountedOrSizedPointerThunkBuilder: ParamBoundsThunkBuilder, PointerBounds
871896
return type
872897
}
873898

874-
func castPointerToOpaquePointer(_ baseAddress: ExprSyntax) throws -> ExprSyntax {
899+
func castPointerToTargetType(_ baseAddress: ExprSyntax) throws -> ExprSyntax {
875900
let type = peelOptionalType(getParam(signature, index).type)
876901
if type.canRepresentBasicType(type: OpaquePointer.self) {
877902
return ExprSyntax("OpaquePointer(\(baseAddress))")
878903
}
904+
if isSizedBy {
905+
if let pointeeType = getPointeeType(type) {
906+
return "\(baseAddress).assumingMemoryBound(to: \(pointeeType).self)"
907+
}
908+
}
879909
return baseAddress
880910
}
881911

@@ -907,7 +937,7 @@ struct CountedOrSizedPointerThunkBuilder: ParamBoundsThunkBuilder, PointerBounds
907937
return unwrappedCall
908938
}
909939

910-
args[index] = try castPointerToOpaquePointer(getPointerArg())
940+
args[index] = try castPointerToTargetType(getPointerArg())
911941
return try base.buildFunctionCall(args)
912942
}
913943
}

test/Interop/C/swiftify-import/Inputs/sized-by-lifetimebound.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#pragma once
22

3+
#include <stdint.h>
4+
5+
#ifndef __sized_by
36
#define __sized_by(x) __attribute__((__sized_by__(x)))
7+
#endif
48
#define __lifetimebound __attribute__((lifetimebound))
59

610
const void * __sized_by(len) simple(int len, int len2, const void * __sized_by(len2) __lifetimebound p);
@@ -18,4 +22,10 @@ const void * __sized_by(len) _Nullable nullable(int len, int len2, const void *
1822
typedef struct foo opaque_t;
1923
opaque_t * __sized_by(len) opaque(int len, int len2, opaque_t * __sized_by(len2) __lifetimebound p);
2024

21-
const void * __sized_by(len) nonsizedLifetime(int len, const void * __lifetimebound p);
25+
const void * __sized_by(len) nonsizedLifetime(int len, const void * __lifetimebound p);
26+
27+
uint8_t *__sized_by(size) bytesized(int size, const uint8_t *__sized_by(size) __lifetimebound);
28+
29+
char *__sized_by(size) charsized(char *__sized_by(size) __lifetimebound, int size);
30+
31+
const uint16_t *__sized_by(size) doublebytesized(uint16_t *__sized_by(size) __lifetimebound, int size);

test/Interop/C/swiftify-import/Inputs/sized-by-noescape.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <stdint.h>
4+
35
#define __sized_by(x) __attribute__((__sized_by__(x)))
46
#define __noescape __attribute__((noescape))
57

@@ -23,3 +25,8 @@ const void * __sized_by(len) __noescape _Nonnull returnPointer(int len);
2325
typedef struct foo opaque_t;
2426
void opaque(int len, opaque_t * __sized_by(len) __noescape p);
2527

28+
void bytesized(int size, const uint8_t *__sized_by(size) __noescape);
29+
30+
void charsized(char *__sized_by(size) __noescape, int size);
31+
32+
void doublebytesized(uint16_t *__sized_by(size) __noescape, int size);

test/Interop/C/swiftify-import/Inputs/sized-by.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#pragma once
22

3+
#include <stdint.h>
4+
5+
#ifndef __sized_by
36
#define __sized_by(x) __attribute__((__sized_by__(x)))
7+
#endif
48

59
void simple(int len, void * __sized_by(len) p);
610

@@ -21,3 +25,15 @@ void * __sized_by(len) returnPointer(int len);
2125

2226
typedef struct foo opaque_t;
2327
void opaque(int len, opaque_t * __sized_by(len) p);
28+
29+
typedef opaque_t *opaqueptr_t;
30+
void opaqueptr(int len, opaqueptr_t __sized_by(len) p);
31+
32+
void charsized(char *__sized_by(size), int size);
33+
34+
uint8_t *__sized_by(size) bytesized(int size);
35+
36+
void doublebytesized(uint16_t *__sized_by(size), int size);
37+
38+
typedef uint8_t * bytesizedptr_t;
39+
void aliasedBytesized(bytesizedptr_t __sized_by(size) p, int size);

test/Interop/C/swiftify-import/counted-by-lifetimebound.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// REQUIRES: swift_feature_SafeInteropWrappers
22
// REQUIRES: swift_feature_LifetimeDependence
33

4-
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByLifetimeboundClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers | %FileCheck %s
4+
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByLifetimeboundClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers -Xcc -Wno-nullability-completeness | %FileCheck %s
55

66
// swift-ide-test doesn't currently typecheck the macro expansions, so run the compiler as well
77
// RUN: %empty-directory(%t)
8-
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByLifetimebound.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence %s
8+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByLifetimebound.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence -strict-memory-safety -warnings-as-errors -Xcc -Werror -Xcc -Wno-nullability-completeness %s
99

1010
// Check that ClangImporter correctly infers and expands @_SwiftifyImport macros for functions with __sized_by __lifetimebound parameters and return values.
1111

@@ -98,7 +98,7 @@ public func callSimple(_ p: inout MutableSpan<CInt>) {
9898
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
9999
@inlinable
100100
public func callNoncountedLifetime(_ p: UnsafeMutablePointer<CInt>) {
101-
let _: MutableSpan<CInt> = noncountedLifetime(73, p)
101+
let _: MutableSpan<CInt> = unsafe noncountedLifetime(73, p)
102102
}
103103

104104
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)

test/Interop/C/swiftify-import/counted-by-no-swiftify.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers | %FileCheck %s
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers -Xcc -Wno-nullability-completeness | %FileCheck %s
22

33
// REQUIRES: swift_feature_SafeInteropWrappers
44

test/Interop/C/swiftify-import/counted-by-noescape.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// REQUIRES: swift_feature_SafeInteropWrappers
22
// REQUIRES: swift_feature_LifetimeDependence
33

4-
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByNoEscapeClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence | %FileCheck %s
4+
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByNoEscapeClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence -Xcc -Wno-nullability-completeness | %FileCheck %s
55

66
// swift-ide-test doesn't currently typecheck the macro expansions, so run the compiler as well
77
// RUN: %empty-directory(%t)
8-
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByNoEscape.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence %s
8+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByNoEscape.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence -strict-memory-safety -warnings-as-errors -Xcc -Werror -Xcc -Wno-nullability-completeness %s
99

1010
// Check that ClangImporter correctly infers and expands @_SwiftifyImport macros for functions with __counted_by __noescape parameters.
1111

@@ -150,14 +150,14 @@ public func callNullable(_ p: inout MutableSpan<CInt>?) {
150150
@lifetime(p: copy p)
151151
@inlinable
152152
public func callReturnLifetimeBound(_ p: inout MutableSpan<CInt>) {
153-
let a: MutableSpan<CInt> = returnLifetimeBound(2, &p)
153+
let _: MutableSpan<CInt> = returnLifetimeBound(2, &p)
154154
}
155155

156156
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
157157
@inlinable
158158
public func callReturnPointer() {
159-
let a: UnsafeMutableBufferPointer<CInt>? = returnPointer(4) // call wrapper
160-
let b: UnsafeMutablePointer<CInt>? = returnPointer(4) // call unsafe interop
159+
let _: UnsafeMutableBufferPointer<CInt>? = returnPointer(4) // call wrapper
160+
let _: UnsafeMutablePointer<CInt>? = returnPointer(4) // call unsafe interop
161161
}
162162

163163
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
@@ -207,7 +207,7 @@ public func callFunc(_ p: inout MutableSpan<CInt>?) {
207207
@lifetime(p: copy p)
208208
@inlinable
209209
public func callFuncRenameKeyword(_ p: inout MutableSpan<CInt>?) {
210-
funcRenamed(func: &p, extension: 1, init: 2, open: 3, var: 4, is: 5, as: 6, in: 7, guard: 8, where: 9)
210+
let _ = funcRenamed(func: &p, extension: 1, init: 2, open: 3, var: 4, is: 5, as: 6, in: 7, guard: 8, where: 9)
211211
}
212212

213213
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)

test/Interop/C/swiftify-import/counted-by.swift

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// REQUIRES: swift_feature_SafeInteropWrappers
22

3-
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers | %FileCheck %s
3+
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers -Xcc -Wno-nullability-completeness | %FileCheck %s
44

55
// swift-ide-test doesn't currently typecheck the macro expansions, so run the compiler as well
66
// RUN: %empty-directory(%t)
7-
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedBy.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers %s
7+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedBy.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -strict-memory-safety -warnings-as-errors -Xcc -Werror -Xcc -Wno-nullability-completeness %s
88

99
// Check that ClangImporter correctly infers and expands @_SwiftifyImport macros for functions with __counted_by parameters.
1010

@@ -67,73 +67,73 @@ import CountedByClang
6767

6868
@inlinable
6969
public func callComplexExpr(_ p: UnsafeMutableBufferPointer<CInt>) {
70-
complexExpr(CInt(p.count), 1, p)
70+
unsafe complexExpr(CInt(p.count), 1, p)
7171
}
7272

7373
@inlinable
7474
public func callConstInt(_ p: UnsafeMutableBufferPointer<CInt>) {
75-
constInt(p)
75+
unsafe constInt(p)
7676
}
7777

7878
@inlinable
7979
public func callNonnull(_ p: UnsafeMutableBufferPointer<CInt>) {
80-
nonnull(p)
80+
unsafe nonnull(p)
8181
}
8282

8383
@inlinable
8484
public func callNullUnspecified(_ p: UnsafeMutableBufferPointer<CInt>) {
85-
nullUnspecified(p)
85+
unsafe nullUnspecified(p)
8686
}
8787

8888
@inlinable
8989
public func callNullable(_ p: UnsafeMutableBufferPointer<CInt>?) {
90-
nullable(p)
90+
unsafe nullable(p)
9191
}
9292

9393
@inlinable
9494
public func callOffByOne(_ p: UnsafeMutableBufferPointer<CInt>) {
95-
offByOne(0, p)
95+
unsafe offByOne(0, p)
9696
}
9797

9898
@inlinable
9999
public func callReturnPointer() {
100-
let a: UnsafeMutableBufferPointer<CInt>? = returnPointer(4) // call wrapper
101-
let b: UnsafeMutablePointer<CInt>? = returnPointer(4) // call unsafe interop
100+
let _: UnsafeMutableBufferPointer<CInt>? = returnPointer(4) // call wrapper
101+
let _: UnsafeMutablePointer<CInt>? = returnPointer(4) // call unsafe interop
102102
}
103103

104104
@inlinable
105105
public func callScalar(_ p: UnsafeMutableBufferPointer<CInt>) {
106-
scalar(4, 2, p)
106+
unsafe scalar(4, 2, p)
107107
}
108108

109109
@inlinable
110110
public func callShared(_ p: UnsafeMutableBufferPointer<CInt>, _ p2: UnsafeMutableBufferPointer<CInt>) {
111-
shared(p, p2)
111+
unsafe shared(p, p2)
112112
}
113113

114114
@inlinable
115115
public func callSimple(_ p: UnsafeMutableBufferPointer<CInt>) {
116-
simple(p)
116+
unsafe simple(p)
117117
}
118118

119119
@inlinable
120120
public func callSimpleIndirectOriginal(_ p: UnsafeMutablePointer<CInt>) {
121-
let f = simple
122-
f(13, p)
121+
let f = unsafe simple
122+
unsafe f(13, p)
123123
}
124124

125125
@inlinable
126126
public func callSimpleIndirectOverload(_ p: UnsafeMutableBufferPointer<CInt>) {
127-
let f: (UnsafeMutableBufferPointer<CInt>) -> Void = simple
128-
f(p)
127+
let f: (UnsafeMutableBufferPointer<CInt>) -> Void = unsafe simple
128+
unsafe f(p)
129129
}
130130

131131
@inlinable
132132
public func callSimpleFlipped(_ p: UnsafeMutableBufferPointer<CInt>) {
133-
simpleFlipped(p)
133+
unsafe simpleFlipped(p)
134134
}
135135

136136
@inlinable
137137
public func callSwiftAttr(_ p: UnsafeMutableBufferPointer<CInt>) {
138-
swiftAttr(p)
138+
unsafe swiftAttr(p)
139139
}

0 commit comments

Comments
 (0)