Skip to content

Commit c9bf39b

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, and fixes some bugs related to macro source file management. rdar://97942270
1 parent 4f32598 commit c9bf39b

13 files changed

+302
-15
lines changed

include/swift/AST/DiagnosticsClangImporter.def

+5
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ NOTE(unresolvable_clang_decl_is_a_framework_bug,none,
8787
WARNING(clang_swift_attr_unhandled,none,
8888
"ignoring unknown Swift attribute or modifier '%0'", (StringRef))
8989

90+
WARNING(clang_pointer_bounds_unhandled, none,
91+
"ignoring unknown pointer bound attributes on '%0' because the "
92+
"generated macro could not be parsed: '%1'",
93+
(DeclName, StringRef))
94+
9095
WARNING(clang_error_code_must_be_sendable,none,
9196
"cannot make error code type '%0' non-sendable because Swift errors "
9297
"are always sendable", (StringRef))

include/swift/AST/TypeCheckRequests.h

+21
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,27 @@ class PrettyPrintDeclRequest
775775
bool isCached() const { return true; }
776776
};
777777

778+
/// Pretty-print the given attribute into a buffer and return a source
779+
/// location that refers to the attribute in that buffer.
780+
class PrettyPrintCustomAttrRequest
781+
: public SimpleRequest<PrettyPrintCustomAttrRequest,
782+
SourceLoc(const CustomAttr *, const Decl *),
783+
RequestFlags::Cached> {
784+
public:
785+
using SimpleRequest::SimpleRequest;
786+
787+
private:
788+
friend SimpleRequest;
789+
790+
// Evaluation.
791+
SourceLoc evaluate(Evaluator &eval, const CustomAttr *attr,
792+
const Decl *decl) const;
793+
794+
public:
795+
// Caching
796+
bool isCached() const { return true; }
797+
};
798+
778799
// Find the type in the cache or look it up
779800
class DefaultTypeRequest
780801
: public SimpleRequest<DefaultTypeRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ SWIFT_REQUEST(TypeChecker, DefaultTypeRequest,
8181
NoLocationInfo)
8282
SWIFT_REQUEST(TypeChecker, PrettyPrintDeclRequest,
8383
SourceLoc(const Decl *), Cached, NoLocationInfo)
84+
SWIFT_REQUEST(TypeChecker, PrettyPrintCustomAttrRequest,
85+
SourceLoc(const CustomAttr *), Cached, NoLocationInfo)
8486
SWIFT_REQUEST(TypeChecker, DifferentiableAttributeTypeCheckRequest,
8587
IndexSubset *(DifferentiableAttr *),
8688
SeparatelyCached, NoLocationInfo)

lib/ClangImporter/ClangImporter.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,10 @@ void importer::getNormalInvocationArguments(
576576
}
577577
}
578578

579+
#ifdef SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS
580+
invokationsArgStrs.push_back("-fexperimental-bounds-safety-attributes");
581+
#endif
582+
579583
// Set C language options.
580584
if (triple.isOSDarwin()) {
581585
invocationArgStrs.insert(invocationArgStrs.end(), {

lib/ClangImporter/ImportDecl.cpp

+102
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc,
125125
genericParams, dc, clangNode);
126126
}
127127
impl.importSwiftAttrAttributes(decl);
128+
impl.importBoundsAttributes(decl);
128129
return decl;
129130
}
130131

@@ -8532,6 +8533,107 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
85328533
}
85338534
}
85348535

8536+
namespace {
8537+
class PointerParamInfo {
8538+
public:
8539+
virtual void print(clang::ASTContext &ctx, llvm::raw_ostream &out) const = 0;
8540+
virtual ~PointerParamInfo() {}
8541+
};
8542+
8543+
class CountedByParam : public PointerParamInfo {
8544+
public:
8545+
size_t pointerIndex;
8546+
clang::Expr *countExpr;
8547+
bool isSizedBy;
8548+
CountedByParam(size_t idx, clang::Expr *E, bool sizedBy)
8549+
: pointerIndex(idx), countExpr(E), isSizedBy(sizedBy) {}
8550+
8551+
virtual void print(clang::ASTContext &ctx,
8552+
llvm::raw_ostream &out) const override {
8553+
out << ".";
8554+
if (isSizedBy)
8555+
out << "sizedBy";
8556+
else
8557+
out << "countedBy";
8558+
out << "(pointer: " << pointerIndex << ", ";
8559+
if (isSizedBy)
8560+
out << "size";
8561+
else
8562+
out << "count";
8563+
out << ": \"";
8564+
countExpr->printPretty(
8565+
out, {}, {ctx.getLangOpts()}); // TODO: map clang::Expr to Swift Expr
8566+
out << "\")";
8567+
}
8568+
virtual ~CountedByParam() {}
8569+
};
8570+
} // namespace
8571+
8572+
void ClangImporter::Implementation::importBoundsAttributes(
8573+
FuncDecl *MappedDecl) {
8574+
auto ClangDecl =
8575+
dyn_cast_or_null<clang::FunctionDecl>(MappedDecl->getClangDecl());
8576+
if (!ClangDecl)
8577+
return;
8578+
8579+
SmallVector<PointerParamInfo *, 4> BoundsInfo;
8580+
size_t parameterIndex = 1;
8581+
for (auto param : ClangDecl->parameters()) {
8582+
if (auto CAT = param->getType()->getAs<clang::CountAttributedType>()) {
8583+
BoundsInfo.push_back(new CountedByParam(
8584+
parameterIndex, CAT->getCountExpr(), CAT->isCountInBytes()));
8585+
}
8586+
parameterIndex++;
8587+
}
8588+
if (BoundsInfo.empty())
8589+
return;
8590+
8591+
llvm::SmallString<128> MacroString;
8592+
{
8593+
llvm::raw_svector_ostream out(MacroString);
8594+
8595+
out << "@PointerBounds(";
8596+
for (size_t i = 0; i < BoundsInfo.size(); i++) {
8597+
BoundsInfo[i]->print(getClangASTContext(), out);
8598+
if (i + 1 < BoundsInfo.size()) {
8599+
out << ", ";
8600+
}
8601+
}
8602+
out << ")";
8603+
}
8604+
8605+
// Dig out a buffer with the attribute text.
8606+
unsigned bufferID = getClangSwiftAttrSourceBuffer(MacroString);
8607+
8608+
// Dig out a source file we can use for parsing.
8609+
auto &sourceFile = getClangSwiftAttrSourceFile(
8610+
*MappedDecl->getDeclContext()->getParentModule(), bufferID);
8611+
8612+
// Spin up a parser.
8613+
swift::Parser parser(bufferID, sourceFile, &SwiftContext.Diags, nullptr,
8614+
nullptr);
8615+
// Prime the lexer.
8616+
parser.consumeTokenWithoutFeedingReceiver();
8617+
8618+
bool hadError = false;
8619+
assert(parser.Tok.is(tok::at_sign));
8620+
SourceLoc atEndLoc = parser.Tok.getRange().getEnd();
8621+
SourceLoc atLoc = parser.consumeToken(tok::at_sign);
8622+
DeclContext *DC = MappedDecl->getParent();
8623+
auto initContext = PatternBindingInitializer::create(DC);
8624+
hadError = parser
8625+
.parseDeclAttribute(MappedDecl->getAttrs(), atLoc, atEndLoc,
8626+
initContext,
8627+
/*isFromClangAttribute=*/true)
8628+
.isError();
8629+
if (hadError) {
8630+
HeaderLoc attrLoc(ClangDecl->getLocation());
8631+
diagnose(attrLoc, diag::clang_pointer_bounds_unhandled,
8632+
MappedDecl->getName(), MacroString);
8633+
return;
8634+
}
8635+
}
8636+
85358637
static bool isUsingMacroName(clang::SourceManager &SM,
85368638
clang::SourceLocation loc,
85378639
StringRef MacroName) {

lib/ClangImporter/ImportType.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -2424,6 +2424,10 @@ ClangImporter::Implementation::importParameterType(
24242424
}
24252425
}
24262426
}
2427+
} else if (auto CAT = dyn_cast<clang::CountAttributedType>(paramTy)) {
2428+
// Treat as a normal pointer. importBoundsAttributes() will generate a safe
2429+
// overload later.
2430+
paramTy = CAT->desugar();
24272431
} else if (isa<clang::PointerType>(paramTy) &&
24282432
isa<clang::TemplateTypeParmType>(paramTy->getPointeeType())) {
24292433
auto pointeeType = paramTy->getPointeeType();

lib/ClangImporter/ImporterImpl.h

+1
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
17321732
}
17331733

17341734
void importSwiftAttrAttributes(Decl *decl);
1735+
void importBoundsAttributes(FuncDecl *MappedDecl);
17351736

17361737
/// Find the lookup table that corresponds to the given Clang module.
17371738
///

lib/Sema/TypeCheckDecl.cpp

+67-3
Original file line numberDiff line numberDiff line change
@@ -3247,9 +3247,8 @@ namespace {
32473247
};
32483248
}
32493249

3250-
SourceLoc PrettyPrintDeclRequest::evaluate(Evaluator &eval, const Decl *decl) const {
3251-
// Conjure a buffer name for this declaration.
3252-
SmallVector<std::string, 4> nameComponents;
3250+
static void collectQualifiedDeclNameComponents(
3251+
const Decl *decl, SmallVectorImpl<std::string> &nameComponents) {
32533252
DeclContext *dc;
32543253
if (auto valueDecl = dyn_cast<ValueDecl>(decl)) {
32553254
nameComponents.push_back(valueDecl->getBaseName().userFacingName().str());
@@ -3296,7 +3295,13 @@ SourceLoc PrettyPrintDeclRequest::evaluate(Evaluator &eval, const Decl *decl) co
32963295

32973296
dc = dc->getParent();
32983297
}
3298+
}
32993299

3300+
SourceLoc PrettyPrintDeclRequest::evaluate(Evaluator &eval,
3301+
const Decl *decl) const {
3302+
// Conjure a buffer name for this declaration.
3303+
SmallVector<std::string, 4> nameComponents;
3304+
collectQualifiedDeclNameComponents(decl, nameComponents);
33003305

33013306
std::string bufferName;
33023307
{
@@ -3391,3 +3396,62 @@ SourceLoc PrettyPrintDeclRequest::evaluate(Evaluator &eval, const Decl *decl) co
33913396

33923397
return memBufferStartLoc.getAdvancedLoc(targetDeclOffsetInBuffer);
33933398
}
3399+
3400+
//----------------------------------------------------------------------------//
3401+
// PrettyPrintCustomAttrRequest
3402+
//----------------------------------------------------------------------------//
3403+
3404+
SourceLoc PrettyPrintCustomAttrRequest::evaluate(Evaluator &eval,
3405+
const CustomAttr *attr,
3406+
const Decl *decl) const {
3407+
// Conjure a buffer name for this declaration.
3408+
SmallVector<std::string, 4> nameComponents;
3409+
nameComponents.push_back(attr->getAttrName().str());
3410+
collectQualifiedDeclNameComponents(decl, nameComponents);
3411+
3412+
std::string bufferName;
3413+
{
3414+
llvm::raw_string_ostream out(bufferName);
3415+
for (auto iter = nameComponents.rbegin(); iter != nameComponents.rend();
3416+
++iter) {
3417+
out << *iter;
3418+
3419+
if (iter + 1 != nameComponents.rend())
3420+
out << ".";
3421+
}
3422+
}
3423+
3424+
// Render the buffer contents.
3425+
ASTContext &ctx = decl->getASTContext();
3426+
llvm::SmallString<128> bufferContents;
3427+
{
3428+
llvm::raw_svector_ostream out(bufferContents);
3429+
StreamPrinter P(out);
3430+
3431+
// Print this macro attribute.
3432+
auto options = PrintOptions::printForDiagnostics(
3433+
getBufferAccessLevel(decl), ctx.TypeCheckerOpts.PrintFullConvention);
3434+
attr->print(P, options, decl);
3435+
}
3436+
3437+
// Build a buffer with the pretty-printed macro attribute.
3438+
SourceManager &sourceMgr = ctx.SourceMgr;
3439+
auto bufferID = sourceMgr.addMemBufferCopy(bufferContents, bufferName);
3440+
auto memBufferStartLoc = sourceMgr.getLocForBufferStart(bufferID);
3441+
3442+
// Note that this is a pretty-printed buffer.
3443+
sourceMgr.setGeneratedSourceInfo(
3444+
bufferID,
3445+
GeneratedSourceInfo{
3446+
GeneratedSourceInfo::PrettyPrinted, CharSourceRange(),
3447+
CharSourceRange(memBufferStartLoc, bufferContents.size()),
3448+
ASTNode(const_cast<Decl *>(decl)).getOpaqueValue(), nullptr});
3449+
3450+
// Add a source file for the buffer.
3451+
auto moduleDecl = decl->getDeclContext()->getParentModule();
3452+
auto sourceFile =
3453+
new (ctx) SourceFile(*moduleDecl, SourceFileKind::Library, bufferID);
3454+
sourceFile->setImports({});
3455+
3456+
return memBufferStartLoc;
3457+
}

lib/Sema/TypeCheckMacros.cpp

+24-12
Original file line numberDiff line numberDiff line change
@@ -1017,21 +1017,21 @@ createMacroSourceFile(std::unique_ptr<llvm::MemoryBuffer> buffer,
10171017
// Create a new source buffer with the contents of the expanded macro.
10181018
unsigned macroBufferID = sourceMgr.addNewSourceBuffer(std::move(buffer));
10191019
auto macroBufferRange = sourceMgr.getRangeForBuffer(macroBufferID);
1020-
GeneratedSourceInfo sourceInfo{generatedSourceKind,
1021-
generatedOriginalSourceRange,
1022-
macroBufferRange,
1023-
target.getOpaqueValue(),
1024-
dc,
1025-
attr,
1026-
macroName.c_str()
1027-
};
1028-
sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo);
1020+
auto macroContext = dc;
10291021

10301022
// Create a source file to hold the macro buffer. This is automatically
10311023
// registered with the enclosing module.
10321024
auto macroSourceFile = new (ctx) SourceFile(
10331025
*dc->getParentModule(), SourceFileKind::MacroExpansion, macroBufferID,
10341026
/*parsingOpts=*/{}, /*isPrimary=*/false);
1027+
if (isa<FileUnit>(macroContext) && !isa<SourceFile>(macroContext))
1028+
macroContext = macroSourceFile;
1029+
GeneratedSourceInfo sourceInfo{
1030+
generatedSourceKind, generatedOriginalSourceRange,
1031+
macroBufferRange, target.getOpaqueValue(),
1032+
macroContext, attr,
1033+
macroName.c_str()};
1034+
sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo);
10351035
if (auto parentSourceFile = dc->getParentSourceFile())
10361036
macroSourceFile->setImports(parentSourceFile->getImports());
10371037
else if (auto clangModuleUnit =
@@ -1359,8 +1359,20 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
13591359

13601360
auto moduleDecl = dc->getParentModule();
13611361

1362-
auto attrSourceFile =
1363-
moduleDecl->getSourceFileContainingLocation(attr->AtLoc);
1362+
// If the attribute has no source location and is attached to a declaration
1363+
// from a Clang module, pretty-print the attribute and use that location.
1364+
// This is relevant for Swift macros inferred from Clang attributes,
1365+
// since they don't have a source representation.
1366+
SourceLoc attrLoc = attr->AtLoc;
1367+
if (attrLoc.isInvalid() &&
1368+
isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
1369+
attrLoc = evaluateOrDefault(ctx.evaluator,
1370+
PrettyPrintCustomAttrRequest{attr, attachedTo},
1371+
SourceLoc());
1372+
assert(attrLoc);
1373+
}
1374+
1375+
auto attrSourceFile = moduleDecl->getSourceFileContainingLocation(attrLoc);
13641376
if (!attrSourceFile)
13651377
return nullptr;
13661378

@@ -1524,7 +1536,7 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
15241536
swift_Macros_expandAttachedMacro(
15251537
&ctx.Diags, externalDef.get(), discriminator->c_str(),
15261538
extendedType.c_str(), conformanceList.c_str(), getRawMacroRole(role),
1527-
astGenAttrSourceFile, attr->AtLoc.getOpaquePointerValue(),
1539+
astGenAttrSourceFile, attrLoc.getOpaquePointerValue(),
15281540
astGenDeclSourceFile, startLoc.getOpaquePointerValue(),
15291541
astGenParentDeclSourceFile, parentDeclLoc, &evaluatedSourceOut);
15301542
if (!evaluatedSourceOut.unbridged().data())

stdlib/cmake/modules/AddSwiftStdlib.cmake

+4
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,10 @@ function(_add_target_variant_c_compile_flags)
478478
list(APPEND result "-DSWIFT_USE_OS_TRACE_LAZY_INIT")
479479
endif()
480480

481+
if(SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS)
482+
list(APPEND result "-DSWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS")
483+
endif()
484+
481485
list(APPEND result ${SWIFT_STDLIB_EXTRA_C_COMPILE_FLAGS})
482486

483487
set("${CFLAGS_RESULT_VAR_NAME}" "${result}" PARENT_SCOPE)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#define __counted_by(x) __attribute__((__counted_by__(x)))
4+
5+
void simple(int len, int * __counted_by(len) p);
6+
7+
void swiftAttr(int len, int * p) __attribute__((swift_attr("@PointerBounds(.countedBy(pointer: 2, count: \"len\"))")));
8+
9+
void shared(int len, int * __counted_by(len) p1, int * __counted_by(len) p2);
10+
11+
// TODO: test this when counted_by expressions land
12+
// void complexExpr(int len, int offset, int * __counted_by(len - offset) p);
13+
14+
void nullUnspecified(int len, int * __counted_by(len) _Null_unspecified p);
15+
16+
void nonnull(int len, int * __counted_by(len) _Nonnull p);
17+
18+
void nullable(int len, int * __counted_by(len) _Nullable p);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module CountedByClang {
2+
header "counted-by.h"
3+
export *
4+
}
5+

0 commit comments

Comments
 (0)