Skip to content

Commit f622a90

Browse files
committed
Infer @PointerBounds macro from clang __counted_by parameters
This results in an automatic wrapper function with safe pointer types when the imported function has bounds attributes. This exercises similar pathways as the recently added functionality for specifying macros from swift_attr. The new functionality is guarded by the experimental language feature SafeInteropWrappers. rdar://97942270
1 parent 41073a8 commit f622a90

File tree

10 files changed

+276
-101
lines changed

10 files changed

+276
-101
lines changed

include/swift/Basic/Features.def

+4
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,10 @@ EXPERIMENTAL_FEATURE(WarnUnsafe, true)
406406
/// Import unsafe C and C++ constructs as @unsafe.
407407
EXPERIMENTAL_FEATURE(SafeInterop, true)
408408

409+
// Import bounds safety and lifetime attributes from interop headers to
410+
// generate Swift wrappers with safe pointer types.
411+
EXPERIMENTAL_FEATURE(SafeInteropWrappers, false)
412+
409413
/// Ignore resilience errors due to C++ types.
410414
EXPERIMENTAL_FEATURE(AssumeResilientCxxTypes, true)
411415

include/swift/Option/Options.td

+5
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,11 @@ def enable_experimental_concise_pound_file : Flag<["-"],
792792
Flags<[FrontendOption, ModuleInterfaceOption]>,
793793
HelpText<"Enable experimental concise '#file' identifier">;
794794

795+
def enable_experimental_bounds_safety_interop :
796+
Flag<["-"], "enable-experimental-bounds-safety-interop">,
797+
Flags<[NoDriverOption, FrontendOption, HelpHidden, ModuleInterfaceOption]>,
798+
HelpText<"Enable experimental C bounds safety interop code generation and config directives">;
799+
795800
def enable_experimental_cxx_interop :
796801
Flag<["-"], "enable-experimental-cxx-interop">,
797802
Flags<[NoDriverOption, FrontendOption, HelpHidden, ModuleInterfaceOption]>,

lib/AST/FeatureSet.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ static bool usesFeatureAllowUnsafeAttribute(Decl *decl) {
321321

322322
UNINTERESTING_FEATURE(WarnUnsafe)
323323
UNINTERESTING_FEATURE(SafeInterop)
324+
UNINTERESTING_FEATURE(SafeInteropWrappers)
324325
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)
325326
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
326327
UNINTERESTING_FEATURE(CoroutineAccessorsAllocateInCallee)

lib/ClangImporter/ClangImporter.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,9 @@ void importer::getNormalInvocationArguments(
578578
}
579579
}
580580

581+
if (LangOpts.hasFeature(Feature::SafeInteropWrappers))
582+
invocationArgStrs.push_back("-fexperimental-bounds-safety-attributes");
583+
581584
// Set C language options.
582585
if (triple.isOSDarwin()) {
583586
invocationArgStrs.insert(invocationArgStrs.end(), {

lib/ClangImporter/ImportDecl.cpp

+134-60
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc,
108108
std::optional<AccessorInfo> accessorInfo, DeclName name,
109109
SourceLoc nameLoc, GenericParamList *genericParams,
110110
ParameterList *bodyParams, Type resultTy, bool async,
111-
bool throws, DeclContext *dc, ClangNode clangNode) {
111+
bool throws, DeclContext *dc, ClangNode clangNode,
112+
bool hasBoundsAnnotation) {
112113
FuncDecl *decl;
113114
if (accessorInfo) {
114115
decl = AccessorDecl::create(
@@ -124,6 +125,9 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc,
124125
genericParams, dc, clangNode);
125126
}
126127
impl.importSwiftAttrAttributes(decl);
128+
if (hasBoundsAnnotation)
129+
impl.importBoundsAttributes(decl);
130+
127131
return decl;
128132
}
129133

@@ -3272,7 +3276,8 @@ namespace {
32723276
}
32733277
return Impl.importFunctionParameterList(
32743278
dc, decl, nonSelfParams, decl->isVariadic(), allowNSUIntegerAsInt,
3275-
argNames, genericParams, /*resultType=*/nullptr);
3279+
argNames, genericParams, /*resultType=*/nullptr,
3280+
/*hasBoundsAnnotatedParam=*/nullptr);
32763281
}
32773282

32783283
Decl *
@@ -3690,6 +3695,7 @@ namespace {
36903695

36913696
bool importFuncWithoutSignature =
36923697
isa<clang::CXXMethodDecl>(decl) && Impl.importSymbolicCXXDecls;
3698+
bool hasBoundsAnnotation = false;
36933699
if (!dc->isModuleScopeContext() && !isa<clang::CXXMethodDecl>(decl)) {
36943700
// Handle initializers.
36953701
if (name.getBaseName().isConstructor()) {
@@ -3786,7 +3792,7 @@ namespace {
37863792
importedType = Impl.importFunctionParamsAndReturnType(
37873793
dc, decl, {decl->param_begin(), decl->param_size()},
37883794
decl->isVariadic(), isInSystemModule(dc), name, bodyParams,
3789-
templateParams);
3795+
templateParams, &hasBoundsAnnotation);
37903796
}
37913797

37923798
if (auto *mdecl = dyn_cast<clang::CXXMethodDecl>(decl)) {
@@ -3853,10 +3859,10 @@ namespace {
38533859
auto resultTy = importedType.getType();
38543860

38553861
FuncDecl *func =
3856-
createFuncOrAccessor(Impl, loc, accessorInfo, name,
3857-
nameLoc, genericParams, bodyParams, resultTy,
3862+
createFuncOrAccessor(Impl, loc, accessorInfo, name, nameLoc,
3863+
genericParams, bodyParams, resultTy,
38583864
/*async=*/false, /*throws=*/false, dc,
3859-
clangNode);
3865+
clangNode, hasBoundsAnnotation);
38603866
result = func;
38613867

38623868
if (!dc->isModuleScopeContext()) {
@@ -4899,12 +4905,14 @@ namespace {
48994905
}
49004906
}
49014907

4902-
auto result = createFuncOrAccessor(Impl,
4903-
/*funcLoc*/ SourceLoc(), accessorInfo,
4904-
importedName.getDeclName(),
4905-
/*nameLoc*/ SourceLoc(),
4906-
/*genericParams=*/nullptr, bodyParams,
4907-
resultTy, async, throws, dc, decl);
4908+
bool hasBoundsAnnotation =
4909+
false; // currently only implemented for functions
4910+
auto result = createFuncOrAccessor(
4911+
Impl,
4912+
/*funcLoc*/ SourceLoc(), accessorInfo, importedName.getDeclName(),
4913+
/*nameLoc*/ SourceLoc(),
4914+
/*genericParams=*/nullptr, bodyParams, resultTy, async, throws, dc,
4915+
decl, hasBoundsAnnotation);
49084916

49094917
result->setAccess(decl->isDirectMethod() ? AccessLevel::Public
49104918
: getOverridableAccessLevel(dc));
@@ -6544,7 +6552,8 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer(
65446552
} else {
65456553
parameterList = Impl.importFunctionParameterList(
65466554
dc, decl, {decl->param_begin(), decl->param_end()}, decl->isVariadic(),
6547-
allowNSUIntegerAsInt, argNames, /*genericParams=*/{}, /*resultType=*/nullptr);
6555+
allowNSUIntegerAsInt, argNames, /*genericParams=*/{},
6556+
/*resultType=*/nullptr, /*hasBoundsAnnotatedParam=*/nullptr);
65486557
}
65496558
if (!parameterList)
65506559
return nullptr;
@@ -8344,6 +8353,53 @@ static bool importAsUnsafe(ClangImporter::Implementation &impl,
83448353
return false;
83458354
}
83468355

8356+
void ClangImporter::Implementation::importNontrivialAttribute(
8357+
Decl *MappedDecl, llvm::StringRef AttrString) {
8358+
bool cached = true;
8359+
while (true) {
8360+
// Dig out a source file we can use for parsing.
8361+
auto &sourceFile = getClangSwiftAttrSourceFile(
8362+
*MappedDecl->getDeclContext()->getParentModule(), AttrString, cached);
8363+
8364+
auto topLevelDecls = sourceFile.getTopLevelDecls();
8365+
8366+
// If we're using the cached version, check whether we can correctly
8367+
// clone the attribute.
8368+
if (cached) {
8369+
bool hasNonclonableAttribute = false;
8370+
for (auto decl : topLevelDecls) {
8371+
if (hasNonclonableAttribute)
8372+
break;
8373+
8374+
for (auto attr : decl->getAttrs()) {
8375+
if (!attr->canClone()) {
8376+
hasNonclonableAttribute = true;
8377+
break;
8378+
}
8379+
}
8380+
}
8381+
8382+
// We cannot clone one of the attributes. Go back and build a new
8383+
// source file without caching it.
8384+
if (hasNonclonableAttribute) {
8385+
cached = false;
8386+
continue;
8387+
}
8388+
}
8389+
8390+
// Collect the attributes from the synthesized top-level declaration in
8391+
// the source file. If we're using a cached copy, clone the attribute.
8392+
for (auto decl : topLevelDecls) {
8393+
SmallVector<DeclAttribute *, 2> attrs(decl->getAttrs().begin(),
8394+
decl->getAttrs().end());
8395+
for (auto attr : attrs) {
8396+
MappedDecl->getAttrs().add(cached ? attr->clone(SwiftContext) : attr);
8397+
}
8398+
}
8399+
break;
8400+
}
8401+
}
8402+
83478403
void
83488404
ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
83498405
auto ClangDecl =
@@ -8481,53 +8537,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
84818537
continue;
84828538
}
84838539

8484-
bool cached = true;
8485-
while (true) {
8486-
// Dig out a source file we can use for parsing.
8487-
auto &sourceFile = getClangSwiftAttrSourceFile(
8488-
*MappedDecl->getDeclContext()->getParentModule(),
8489-
swiftAttr->getAttribute(),
8490-
cached);
8491-
8492-
auto topLevelDecls = sourceFile.getTopLevelDecls();
8493-
8494-
// If we're using the cached version, check whether we can correctly
8495-
// clone the attribute.
8496-
if (cached) {
8497-
bool hasNonclonableAttribute = false;
8498-
for (auto decl : topLevelDecls) {
8499-
if (hasNonclonableAttribute)
8500-
break;
8501-
8502-
for (auto attr : decl->getAttrs()) {
8503-
if (!attr->canClone()) {
8504-
hasNonclonableAttribute = true;
8505-
break;
8506-
}
8507-
}
8508-
}
8509-
8510-
// We cannot clone one of the attributes. Go back and build a new
8511-
// source file without caching it.
8512-
if (hasNonclonableAttribute) {
8513-
cached = false;
8514-
continue;
8515-
}
8516-
}
8517-
8518-
// Collect the attributes from the synthesized top-level declaration in
8519-
// the source file. If we're using a cached copy, clone the attribute.
8520-
for (auto decl : topLevelDecls) {
8521-
SmallVector<DeclAttribute *, 2> attrs(decl->getAttrs().begin(),
8522-
decl->getAttrs().end());
8523-
for (auto attr : attrs) {
8524-
MappedDecl->getAttrs().add(cached ? attr->clone(SwiftContext)
8525-
: attr);
8526-
}
8527-
}
8528-
8529-
break;
8530-
}
8540+
importNontrivialAttribute(MappedDecl, swiftAttr->getAttribute());
85318541
}
85328542

85338543
if (seenUnsafe || importAsUnsafe(*this, ClangDecl, MappedDecl)) {
@@ -8613,6 +8623,70 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
86138623
}
86148624
}
86158625

8626+
namespace {
8627+
class PointerParamInfoPrinter {
8628+
public:
8629+
clang::ASTContext &ctx;
8630+
llvm::raw_ostream &out;
8631+
bool firstParam = true;
8632+
PointerParamInfoPrinter(clang::ASTContext &ctx, llvm::raw_ostream &out)
8633+
: ctx(ctx), out(out) {
8634+
out << "@PointerBounds(";
8635+
}
8636+
~PointerParamInfoPrinter() { out << ")"; }
8637+
8638+
void printCountedBy(const clang::CountAttributedType *CAT,
8639+
size_t pointerIndex) {
8640+
if (!firstParam) {
8641+
out << ", ";
8642+
} else {
8643+
firstParam = false;
8644+
}
8645+
clang::Expr *countExpr = CAT->getCountExpr();
8646+
bool isSizedBy = CAT->isCountInBytes();
8647+
out << ".";
8648+
if (isSizedBy)
8649+
out << "sizedBy";
8650+
else
8651+
out << "countedBy";
8652+
out << "(pointer: " << pointerIndex + 1 << ", ";
8653+
if (isSizedBy)
8654+
out << "size";
8655+
else
8656+
out << "count";
8657+
out << ": \"";
8658+
countExpr->printPretty(
8659+
out, {}, {ctx.getLangOpts()}); // TODO: map clang::Expr to Swift Expr
8660+
out << "\")";
8661+
}
8662+
};
8663+
} // namespace
8664+
8665+
void ClangImporter::Implementation::importBoundsAttributes(
8666+
FuncDecl *MappedDecl) {
8667+
assert(SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers));
8668+
auto ClangDecl =
8669+
dyn_cast_or_null<clang::FunctionDecl>(MappedDecl->getClangDecl());
8670+
// any function with safe pointer imports should have a clang decl
8671+
assert(ClangDecl);
8672+
if (!ClangDecl)
8673+
return;
8674+
8675+
llvm::SmallString<128> MacroString;
8676+
{
8677+
llvm::raw_svector_ostream out(MacroString);
8678+
8679+
PointerParamInfoPrinter printer(getClangASTContext(), out);
8680+
for (auto [index, param] : llvm::enumerate(ClangDecl->parameters())) {
8681+
if (auto CAT = param->getType()->getAs<clang::CountAttributedType>()) {
8682+
printer.printCountedBy(CAT, index);
8683+
}
8684+
}
8685+
}
8686+
8687+
importNontrivialAttribute(MappedDecl, MacroString);
8688+
}
8689+
86168690
static bool isUsingMacroName(clang::SourceManager &SM,
86178691
clang::SourceLocation loc,
86188692
StringRef MacroName) {

0 commit comments

Comments
 (0)