Skip to content

[Swiftify] Do not swiftify non-Swift-like counted_by exprs #81395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjCCommon.h"
#include "clang/AST/Expr.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
Expand Down Expand Up @@ -9139,6 +9141,51 @@ class SwiftifyInfoPrinter {
};
} // namespace

namespace {
struct CountedByExpressionValidator
: clang::ConstStmtVisitor<CountedByExpressionValidator, bool> {
bool VisitDeclRefExpr(const clang::DeclRefExpr *e) { return true; }
bool VisitIntegerLiteral(const clang::IntegerLiteral *) { return true; }
bool VisitImplicitCastExpr(const clang::ImplicitCastExpr *c) {
return this->Visit(c->getSubExpr());
}
bool VisitParenExpr(const clang::ParenExpr *p) {
return this->Visit(p->getSubExpr());
}

#define SUPPORTED_UNOP(UNOP) \
bool VisitUnary ## UNOP(const clang::UnaryOperator *unop) { \
return this->Visit(unop->getSubExpr()); \
}
SUPPORTED_UNOP(Plus)
SUPPORTED_UNOP(Minus)
SUPPORTED_UNOP(Not)
#undef SUPPORTED_UNOP

#define SUPPORTED_BINOP(BINOP) \
bool VisitBin ## BINOP(const clang::BinaryOperator *binop) { \
return this->Visit(binop->getLHS()) && this->Visit(binop->getRHS()); \
}
SUPPORTED_BINOP(Add)
SUPPORTED_BINOP(Sub)
SUPPORTED_BINOP(Div)
SUPPORTED_BINOP(Mul)
SUPPORTED_BINOP(Rem)
SUPPORTED_BINOP(Shl)
SUPPORTED_BINOP(Shr)
SUPPORTED_BINOP(And)
SUPPORTED_BINOP(Xor)
SUPPORTED_BINOP(Or)
#undef SUPPORTED_BINOP

bool VisitStmt(const clang::Stmt *) { return false; }
};
} // namespace

static bool SwiftifiableCAT(const clang::CountAttributedType *CAT) {
return CAT && CountedByExpressionValidator().Visit(CAT->getCountExpr());
}

void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
return;
Expand Down Expand Up @@ -9174,8 +9221,8 @@ void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
SwiftifyInfoPrinter printer(getClangASTContext(), SwiftContext, out);
bool returnIsStdSpan = registerStdSpanTypeMapping(
MappedDecl->getResultInterfaceType(), ClangDecl->getReturnType());
if (auto CAT =
ClangDecl->getReturnType()->getAs<clang::CountAttributedType>()) {
auto *CAT = ClangDecl->getReturnType()->getAs<clang::CountAttributedType>();
if (SwiftifiableCAT(CAT)) {
printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX);
attachMacro = true;
}
Expand All @@ -9190,7 +9237,8 @@ void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
auto clangParamTy = clangParam->getType();
auto swiftParam = MappedDecl->getParameters()->get(index);
bool paramHasBoundsInfo = false;
if (auto CAT = clangParamTy->getAs<clang::CountAttributedType>()) {
auto *CAT = clangParamTy->getAs<clang::CountAttributedType>();
if (SwiftifiableCAT(CAT)) {
printer.printCountedBy(CAT, index);
attachMacro = paramHasBoundsInfo = true;
}
Expand Down
34 changes: 34 additions & 0 deletions test/Interop/C/swiftify-import/Inputs/counted-by.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

void simple(int len, int * __counted_by(len) p);

void simpleFlipped(int * __counted_by(len) p, int len);

void swiftAttr(int len, int *p) __attribute__((
swift_attr("@_SwiftifyImport(.countedBy(pointer: .param(2), count: \"len\"))")));

Expand All @@ -18,3 +20,35 @@ void nonnull(int len, int * __counted_by(len) _Nonnull p);
void nullable(int len, int * __counted_by(len) _Nullable p);

int * __counted_by(len) returnPointer(int len);

void offByOne(int len, int * __counted_by(len + 1) p);

void offBySome(int len, int offset, int * __counted_by(len + (1 + offset)) p);

void scalar(int m, int n, int * __counted_by(m * n) p);

void bitwise(int m, int n, int o, int * __counted_by(m & n | ~o) p);

void bitshift(int m, int n, int o, int * __counted_by(m << (n >> o)) p);

void constInt(int * __counted_by(42 * 10) p);

void constFloatCastedToInt(int * __counted_by((int) (4.2 / 12)) p);

void sizeofType(int * __counted_by(sizeof(int *)) p);

void sizeofParam(int * __counted_by(sizeof(p)) p);

void derefLen(int * len, int * __counted_by(*len) p);

void lNot(int len, int * __counted_by(!len) p);

void lAnd(int len, int * __counted_by(len && len) p);

void lOr(int len, int * __counted_by(len || len) p);

void floatCastToInt(float meters, int * __counted_by((int) meters) p);

void pointerCastToInt(int *square, int * __counted_by((int) square) p);

void nanAsInt(int * __counted_by((int) (0 / 0)) p);
14 changes: 14 additions & 0 deletions test/Interop/C/swiftify-import/counted-by-no-swiftify.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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

// REQUIRES: swift_feature_SafeInteropWrappers

// These functions use __counted_by annotations that are not syntactically valid
// in Swift, so they should not be Swiftified

// CHECK-NOT: @_alwaysEmitIntoClient {{.*}} derefLen
// CHECK-NOT: @_alwaysEmitIntoClient {{.*}} lNot
// CHECK-NOT: @_alwaysEmitIntoClient {{.*}} lAnd
// CHECK-NOT: @_alwaysEmitIntoClient {{.*}} lOr
// CHECK-NOT: @_alwaysEmitIntoClient {{.*}} floatCastToInt
// CHECK-NOT: @_alwaysEmitIntoClient {{.*}} pointerCastToInt
// CHECK-NOT: @_alwaysEmitIntoClient {{.*}} nanAsInt
32 changes: 31 additions & 1 deletion test/Interop/C/swiftify-import/counted-by.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,35 @@

import CountedByClang

// CHECK: @_alwaysEmitIntoClient public func complexExpr(_ len: Int{{.*}}, _ offset: Int{{.*}}, _ p: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK: @_alwaysEmitIntoClient public func bitshift(_ m: Int32, _ n: Int32, _ o: Int32, _ p: UnsafeMutableBufferPointer<Int32>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func bitwise(_ m: Int32, _ n: Int32, _ o: Int32, _ p: UnsafeMutableBufferPointer<Int32>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func complexExpr(_ len: Int{{.*}}, _ offset: Int{{.*}}, _ p: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func constFloatCastedToInt(_ p: UnsafeMutableBufferPointer<Int32>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func constInt(_ p: UnsafeMutableBufferPointer<Int32>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullable(_ p: UnsafeMutableBufferPointer<Int{{.*}}>?)
// CHECK-NEXT: @_alwaysEmitIntoClient public func offByOne(_ len: Int32, _ p: UnsafeMutableBufferPointer<Int32>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func offBySome(_ len: Int32, _ offset: Int32, _ p: UnsafeMutableBufferPointer<Int32>)
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func returnPointer(_ len: Int{{.*}}) -> UnsafeMutableBufferPointer<Int{{.*}}>
// CHECK-NEXT: @_alwaysEmitIntoClient public func scalar(_ m: Int32, _ n: Int32, _ p: UnsafeMutableBufferPointer<Int32>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int{{.*}}, _ p1: UnsafeMutableBufferPointer<Int{{.*}}>, _ p2: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func simpleFlipped(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func sizeofParam(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func sizeofType(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
// CHECK-NEXT: @_alwaysEmitIntoClient public func swiftAttr(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)

@inlinable
public func callComplexExpr(_ p: UnsafeMutableBufferPointer<CInt>) {
complexExpr(CInt(p.count), 1, p)
}

@inlinable
public func callConstInt(_ p: UnsafeMutableBufferPointer<CInt>) {
constInt(p)
}

@inlinable
public func callNonnull(_ p: UnsafeMutableBufferPointer<CInt>) {
nonnull(p)
Expand All @@ -39,12 +54,22 @@ public func callNullable(_ p: UnsafeMutableBufferPointer<CInt>?) {
nullable(p)
}

@inlinable
public func callOffByOne(_ p: UnsafeMutableBufferPointer<CInt>) {
offByOne(0, p)
}

@inlinable
public func callReturnPointer() {
let a: UnsafeMutableBufferPointer<CInt>? = returnPointer(4) // call wrapper
let b: UnsafeMutablePointer<CInt>? = returnPointer(4) // call unsafe interop
}

@inlinable
public func callScalar(_ p: UnsafeMutableBufferPointer<CInt>) {
scalar(4, 2, p)
}

@inlinable
public func callShared(_ p: UnsafeMutableBufferPointer<CInt>, _ p2: UnsafeMutableBufferPointer<CInt>) {
shared(CInt(p.count), p, p2)
Expand All @@ -55,6 +80,11 @@ public func callSimple(_ p: UnsafeMutableBufferPointer<CInt>) {
simple(p)
}

@inlinable
public func callSimpleFlipped(_ p: UnsafeMutableBufferPointer<CInt>) {
simpleFlipped(p)
}

@inlinable
public func callSwiftAttr(_ p: UnsafeMutableBufferPointer<CInt>) {
swiftAttr(p)
Expand Down