Skip to content

Commit 7b2d577

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 bf4e60e commit 7b2d577

File tree

13 files changed

+310
-21
lines changed

13 files changed

+310
-21
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 5 additions & 0 deletions
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

Lines changed: 21 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ void importer::getNormalInvocationArguments(
574574
}
575575
}
576576

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

lib/ClangImporter/ImportDecl.cpp

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ createFuncOrAccessor(ClangImporter::Implementation &impl, SourceLoc funcLoc,
122122
genericParams, dc, clangNode);
123123
}
124124
impl.importSwiftAttrAttributes(decl);
125+
impl.importBoundsAttributes(decl);
125126
return decl;
126127
}
127128

@@ -8138,13 +8139,13 @@ unsigned ClangImporter::Implementation::getClangSwiftAttrSourceBuffer(
81388139

81398140
SourceFile &ClangImporter::Implementation::getClangSwiftAttrSourceFile(
81408141
ModuleDecl &module, unsigned bufferID) {
8141-
auto known = ClangSwiftAttrSourceFiles.find(&module);
8142+
auto known = ClangSwiftAttrSourceFiles.find(bufferID);
81428143
if (known != ClangSwiftAttrSourceFiles.end())
81438144
return *known->second;
81448145

81458146
auto sourceFile = new (SwiftContext)
81468147
SourceFile(module, SourceFileKind::Library, bufferID);
8147-
ClangSwiftAttrSourceFiles.insert({&module, sourceFile});
8148+
ClangSwiftAttrSourceFiles.insert({bufferID, sourceFile});
81488149

81498150
return *sourceFile;
81508151
}
@@ -8437,6 +8438,107 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
84378438
}
84388439
}
84398440

8441+
namespace {
8442+
class PointerParamInfo {
8443+
public:
8444+
virtual void print(clang::ASTContext &ctx, llvm::raw_ostream &out) const = 0;
8445+
virtual ~PointerParamInfo() {}
8446+
};
8447+
8448+
class CountedByParam : public PointerParamInfo {
8449+
public:
8450+
size_t pointerIndex;
8451+
clang::Expr *countExpr;
8452+
bool isSizedBy;
8453+
CountedByParam(size_t idx, clang::Expr *E, bool sizedBy)
8454+
: pointerIndex(idx), countExpr(E), isSizedBy(sizedBy) {}
8455+
8456+
virtual void print(clang::ASTContext &ctx,
8457+
llvm::raw_ostream &out) const override {
8458+
out << ".";
8459+
if (isSizedBy)
8460+
out << "sizedBy";
8461+
else
8462+
out << "countedBy";
8463+
out << "(pointer: " << pointerIndex << ", ";
8464+
if (isSizedBy)
8465+
out << "size";
8466+
else
8467+
out << "count";
8468+
out << ": \"";
8469+
countExpr->printPretty(
8470+
out, {}, {ctx.getLangOpts()}); // TODO: map clang::Expr to Swift Expr
8471+
out << "\")";
8472+
}
8473+
virtual ~CountedByParam() {}
8474+
};
8475+
} // namespace
8476+
8477+
void ClangImporter::Implementation::importBoundsAttributes(
8478+
FuncDecl *MappedDecl) {
8479+
auto ClangDecl =
8480+
dyn_cast_or_null<clang::FunctionDecl>(MappedDecl->getClangDecl());
8481+
if (!ClangDecl)
8482+
return;
8483+
8484+
SmallVector<PointerParamInfo *, 4> BoundsInfo;
8485+
size_t parameterIndex = 1;
8486+
for (auto param : ClangDecl->parameters()) {
8487+
if (auto CAT = param->getType()->getAs<clang::CountAttributedType>()) {
8488+
BoundsInfo.push_back(new CountedByParam(
8489+
parameterIndex, CAT->getCountExpr(), CAT->isCountInBytes()));
8490+
}
8491+
parameterIndex++;
8492+
}
8493+
if (BoundsInfo.empty())
8494+
return;
8495+
8496+
llvm::SmallString<128> MacroString;
8497+
{
8498+
llvm::raw_svector_ostream out(MacroString);
8499+
8500+
out << "@PointerBounds(";
8501+
for (size_t i = 0; i < BoundsInfo.size(); i++) {
8502+
BoundsInfo[i]->print(getClangASTContext(), out);
8503+
if (i + 1 < BoundsInfo.size()) {
8504+
out << ", ";
8505+
}
8506+
}
8507+
out << ")";
8508+
}
8509+
8510+
// Dig out a buffer with the attribute text.
8511+
unsigned bufferID = getClangSwiftAttrSourceBuffer(MacroString);
8512+
8513+
// Dig out a source file we can use for parsing.
8514+
auto &sourceFile = getClangSwiftAttrSourceFile(
8515+
*MappedDecl->getDeclContext()->getParentModule(), bufferID);
8516+
8517+
// Spin up a parser.
8518+
swift::Parser parser(bufferID, sourceFile, &SwiftContext.Diags, nullptr,
8519+
nullptr);
8520+
// Prime the lexer.
8521+
parser.consumeTokenWithoutFeedingReceiver();
8522+
8523+
bool hadError = false;
8524+
assert(parser.Tok.is(tok::at_sign));
8525+
SourceLoc atEndLoc = parser.Tok.getRange().getEnd();
8526+
SourceLoc atLoc = parser.consumeToken(tok::at_sign);
8527+
DeclContext *DC = MappedDecl->getParent();
8528+
auto initContext = PatternBindingInitializer::create(DC);
8529+
hadError = parser
8530+
.parseDeclAttribute(MappedDecl->getAttrs(), atLoc, atEndLoc,
8531+
initContext,
8532+
/*isFromClangAttribute=*/true)
8533+
.isError();
8534+
if (hadError) {
8535+
HeaderLoc attrLoc(ClangDecl->getLocation());
8536+
diagnose(attrLoc, diag::clang_pointer_bounds_unhandled,
8537+
MappedDecl->getName(), MacroString);
8538+
return;
8539+
}
8540+
}
8541+
84408542
static bool isUsingMacroName(clang::SourceManager &SM,
84418543
clang::SourceLocation loc,
84428544
StringRef MacroName) {

lib/ClangImporter/ImportType.cpp

Lines changed: 4 additions & 0 deletions
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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,9 +541,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
541541
/// Swift attributes on import.
542542
llvm::StringMap<unsigned> ClangSwiftAttrSourceBuffers;
543543

544-
/// Mapping from modules in which a Clang swift_attr attribute occurs, to be
544+
/// Mapping from buffer ID in which a Clang swift_attr attribute occurs, to be
545545
/// used when parsing the attribute text.
546-
llvm::SmallDenseMap<ModuleDecl *, SourceFile *> ClangSwiftAttrSourceFiles;
546+
llvm::SmallDenseMap<unsigned, SourceFile *> ClangSwiftAttrSourceFiles;
547547

548548
public:
549549
/// The Swift lookup table for the bridging header.
@@ -1723,6 +1723,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
17231723
}
17241724

17251725
void importSwiftAttrAttributes(Decl *decl);
1726+
void importBoundsAttributes(FuncDecl *MappedDecl);
17261727

17271728
/// Find the lookup table that corresponds to the given Clang module.
17281729
///

lib/Sema/TypeCheckDecl.cpp

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3246,9 +3246,8 @@ namespace {
32463246
};
32473247
}
32483248

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

32963295
dc = dc->getParent();
32973296
}
3297+
}
32983298

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

33003305
std::string bufferName;
33013306
{
@@ -3390,3 +3395,62 @@ SourceLoc PrettyPrintDeclRequest::evaluate(Evaluator &eval, const Decl *decl) co
33903395

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

lib/Sema/TypeCheckMacros.cpp

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,25 +1008,27 @@ createMacroSourceFile(std::unique_ptr<llvm::MemoryBuffer> buffer,
10081008
// Create a new source buffer with the contents of the expanded macro.
10091009
unsigned macroBufferID = sourceMgr.addNewSourceBuffer(std::move(buffer));
10101010
auto macroBufferRange = sourceMgr.getRangeForBuffer(macroBufferID);
1011-
GeneratedSourceInfo sourceInfo{generatedSourceKind,
1012-
generatedOriginalSourceRange,
1013-
macroBufferRange,
1014-
target.getOpaqueValue(),
1015-
dc,
1016-
attr,
1017-
macroName.c_str()
1018-
};
1019-
sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo);
1011+
auto macroContext = dc;
10201012

10211013
// Create a source file to hold the macro buffer. This is automatically
10221014
// registered with the enclosing module.
10231015
auto macroSourceFile = new (ctx) SourceFile(
10241016
*dc->getParentModule(), SourceFileKind::MacroExpansion, macroBufferID,
10251017
/*parsingOpts=*/{}, /*isPrimary=*/false);
1018+
if (isa<FileUnit>(macroContext) && !isa<SourceFile>(macroContext))
1019+
macroContext = macroSourceFile;
1020+
GeneratedSourceInfo sourceInfo{
1021+
generatedSourceKind, generatedOriginalSourceRange,
1022+
macroBufferRange, target.getOpaqueValue(),
1023+
macroContext, attr,
1024+
macroName.c_str()};
1025+
sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo);
10261026
if (auto parentSourceFile = dc->getParentSourceFile())
10271027
macroSourceFile->setImports(parentSourceFile->getImports());
1028-
else if (isa<ClangModuleUnit>(dc->getModuleScopeContext()))
1029-
macroSourceFile->setImports({});
1028+
else if (auto CMU = dyn_cast<ClangModuleUnit>(dc->getModuleScopeContext())) {
1029+
auto stdlib = ctx.getStdlibModule(/*loadIfAbsent*/ true);
1030+
macroSourceFile->setImports({ImportedModule(stdlib)});
1031+
}
10301032
return macroSourceFile;
10311033
}
10321034

@@ -1346,8 +1348,20 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
13461348

13471349
auto moduleDecl = dc->getParentModule();
13481350

1349-
auto attrSourceFile =
1350-
moduleDecl->getSourceFileContainingLocation(attr->AtLoc);
1351+
// If the attribute has no source location and is attached to a declaration
1352+
// from a Clang module, pretty-print the attribute and use that location.
1353+
// This is relevant for Swift macros inferred from Clang attributes,
1354+
// since they don't have a source representation.
1355+
SourceLoc attrLoc = attr->AtLoc;
1356+
if (attrLoc.isInvalid() &&
1357+
isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
1358+
attrLoc = evaluateOrDefault(ctx.evaluator,
1359+
PrettyPrintCustomAttrRequest{attr, attachedTo},
1360+
SourceLoc());
1361+
assert(attrLoc);
1362+
}
1363+
1364+
auto attrSourceFile = moduleDecl->getSourceFileContainingLocation(attrLoc);
13511365
if (!attrSourceFile)
13521366
return nullptr;
13531367

@@ -1510,7 +1524,7 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
15101524
swift_Macros_expandAttachedMacro(
15111525
&ctx.Diags, externalDef.get(), discriminator->c_str(),
15121526
extendedType.c_str(), conformanceList.c_str(), getRawMacroRole(role),
1513-
astGenAttrSourceFile, attr->AtLoc.getOpaquePointerValue(),
1527+
astGenAttrSourceFile, attrLoc.getOpaquePointerValue(),
15141528
astGenDeclSourceFile, startLoc.getOpaquePointerValue(),
15151529
astGenParentDeclSourceFile, parentDeclLoc, &evaluatedSourceOut);
15161530
if (!evaluatedSourceOut.unbridged().data())

stdlib/cmake/modules/AddSwiftStdlib.cmake

Lines changed: 4 additions & 0 deletions
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)

0 commit comments

Comments
 (0)