Skip to content

Commit 9e89957

Browse files
committed
[ClangImporter] Attach _SwiftifyImportProtocol to imported protocols
with bounds attributes This creates safe overloads for any methods in the protocol annotated with bounds information. rdar://144335990
1 parent e3009d5 commit 9e89957

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1168
-340
lines changed

lib/AST/ConformanceLookup.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,22 @@ static ProtocolConformanceRef getPackTypeConformance(
533533
PackConformance::get(type, protocol, patternConformances));
534534
}
535535

536+
static bool shouldExpandExtensionMacro(Evaluator &evaluator, NominalTypeDecl *nominal) {
537+
if (evaluator.hasActiveRequest(ExpandExtensionMacros{nominal})) {
538+
return false;
539+
}
540+
541+
// Expanding an extension macro for this type will require pretty printing the node,
542+
// leading to evaluation of it's members. Once the macro expansion requrest has started
543+
// it's too late to check for cycles.
544+
for (auto member : nominal->getMembers())
545+
if (auto VD = dyn_cast<ValueDecl>(member))
546+
if (evaluator.hasActiveRequest(InterfaceTypeRequest{VD}))
547+
return false;
548+
549+
return true;
550+
}
551+
536552
ProtocolConformanceRef
537553
LookupConformanceInModuleRequest::evaluate(
538554
Evaluator &evaluator, LookupConformanceDescriptor desc) const {
@@ -670,10 +686,11 @@ LookupConformanceInModuleRequest::evaluate(
670686
// extension macro can generate a conformance to the
671687
// given protocol, but conformance macros do not specify
672688
// that information upfront.
673-
(void)evaluateOrDefault(
674-
ctx.evaluator,
675-
ExpandExtensionMacros{nominal},
676-
{ });
689+
if (shouldExpandExtensionMacro(ctx.evaluator, nominal))
690+
(void)evaluateOrDefault(
691+
evaluator,
692+
ExpandExtensionMacros{nominal},
693+
{ });
677694

678695
// Find the root conformance in the nominal type declaration's
679696
// conformance lookup table.

lib/ClangImporter/ImportDecl.cpp

Lines changed: 197 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "CFTypeInfo.h"
18+
#include "ClangAdapter.h"
1819
#include "ClangDerivedConformances.h"
1920
#include "ImporterImpl.h"
2021
#include "SwiftDeclSynthesizer.h"
2122
#include "swift/AST/ASTContext.h"
23+
#include "swift/AST/ASTPrinter.h"
2224
#include "swift/AST/Attr.h"
2325
#include "swift/AST/AvailabilityInference.h"
2426
#include "swift/AST/Builtins.h"
@@ -5810,6 +5812,8 @@ namespace {
58105812

58115813
result->setMemberLoader(&Impl, 0);
58125814

5815+
Impl.swiftifyProtocol(result);
5816+
58135817
return result;
58145818
}
58155819

@@ -6031,6 +6035,8 @@ namespace {
60316035
}
60326036
}
60336037
}
6038+
6039+
Impl.swiftifyProtocol(result);
60346040
return result;
60356041
}
60366042

@@ -9039,18 +9045,25 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
90399045
}
90409046

90419047
namespace {
9048+
class SwiftifyInfoPrinter;
9049+
static bool swiftifyImpl(SwiftifyInfoPrinter &printer, const FuncDecl *MappedDecl);
9050+
90429051
class SwiftifyInfoPrinter {
90439052
public:
90449053
static const ssize_t SELF_PARAM_INDEX = -2;
90459054
static const ssize_t RETURN_VALUE_INDEX = -1;
9055+
9056+
protected:
90469057
clang::ASTContext &ctx;
9058+
ASTContext &SwiftContext;
90479059
llvm::raw_ostream &out;
90489060
bool firstParam = true;
90499061
llvm::StringMap<std::string> typeMapping;
90509062

9051-
SwiftifyInfoPrinter(clang::ASTContext &ctx, llvm::raw_ostream &out)
9052-
: ctx(ctx), out(out) {
9053-
out << "@_SwiftifyImport(";
9063+
public:
9064+
SwiftifyInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, llvm::raw_ostream &out)
9065+
: ctx(ctx), SwiftContext(SwiftContext), out(out) {
9066+
out << "(";
90549067
}
90559068
~SwiftifyInfoPrinter() { out << ")"; }
90569069

@@ -9116,15 +9129,20 @@ class SwiftifyInfoPrinter {
91169129
out << "]";
91179130
}
91189131

9119-
private:
9120-
void printSeparator() {
9121-
if (!firstParam) {
9122-
out << ", ";
9123-
} else {
9124-
firstParam = false;
9132+
void printAvailability() {
9133+
printSeparator();
9134+
out << "availability: [";
9135+
if (!SwiftContext.LangOpts.Target.isOSDarwin() && !SwiftContext.LangOpts.Target.isDriverKit()) {
9136+
out << ":]";
9137+
return;
91259138
}
9139+
printAvailabilityOfType("Span");
9140+
printSeparator();
9141+
printAvailabilityOfType("MutableSpan");
9142+
out << "]";
91269143
}
91279144

9145+
private:
91289146
void printParamOrReturn(ssize_t pointerIndex) {
91299147
if (pointerIndex == SELF_PARAM_INDEX)
91309148
out << ".self";
@@ -9133,75 +9151,191 @@ class SwiftifyInfoPrinter {
91339151
else
91349152
out << ".param(" << pointerIndex + 1 << ")";
91359153
}
9154+
9155+
ValueDecl *getDecl(StringRef DeclName) {
9156+
SmallVector<ValueDecl *, 1> decls;
9157+
SwiftContext.lookupInSwiftModule(DeclName, decls);
9158+
assert(decls.size() == 1);
9159+
if (decls.size() != 1) return nullptr;
9160+
return decls[0];
9161+
}
9162+
9163+
StringRef getDarwinOSString() {
9164+
auto Target = SwiftContext.LangOpts.Target;
9165+
if (Target.isMacOSX()) {
9166+
return "macOS";
9167+
} else if (Target.isTvOS()) {
9168+
return "tvOS";
9169+
} else if (Target.isiOS()) {
9170+
return "iOS";
9171+
} else if (Target.isWatchOS()) {
9172+
return "watchOS";
9173+
} else if (Target.isXROS()) {
9174+
return "visionOS";
9175+
}
9176+
llvm_unreachable("unknown darwin OS");
9177+
}
9178+
9179+
void printAvailabilityOfType(StringRef Name) {
9180+
ValueDecl *D = getDecl(Name);
9181+
auto Availability = AvailabilityInference::availableRange(D);
9182+
9183+
out << "\"" << Name << "\":" << "\"" << getDarwinOSString() << " " << Availability.getVersionString() << "\"";
9184+
}
9185+
9186+
protected:
9187+
void printSeparator() {
9188+
if (!firstParam) {
9189+
out << ", ";
9190+
} else {
9191+
firstParam = false;
9192+
}
9193+
}
91369194
};
9137-
} // namespace
91389195

9139-
void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
9140-
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
9141-
return;
9142-
auto ClangDecl =
9143-
dyn_cast_or_null<clang::FunctionDecl>(MappedDecl->getClangDecl());
9144-
if (!ClangDecl)
9145-
return;
9196+
class SwiftifyProtocolInfoPrinter : public SwiftifyInfoPrinter {
9197+
public:
9198+
SwiftifyProtocolInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, llvm::raw_ostream &out)
9199+
: SwiftifyInfoPrinter(ctx, SwiftContext, out) {}
9200+
9201+
bool printMethod(const FuncDecl *Method) {
9202+
// don't emit .method() if we know it's going to be empty
9203+
if (!isa_and_nonnull<clang::ObjCMethodDecl>(Method->getClangDecl()))
9204+
return false;
9205+
9206+
printSeparator();
9207+
out << ".method(signature: \"";
9208+
printMethodSignature(Method);
9209+
out << "\", paramInfo: [";
9210+
// reset firstParam inside paramInfo array. At this point firstParam will
9211+
// always be false, so no need to save the current value.
9212+
assert(!firstParam);
9213+
firstParam = true;
9214+
bool hadAttributes = swiftifyImpl(*this, Method);
9215+
firstParam = false;
9216+
out << "])";
9217+
return hadAttributes;
9218+
}
9219+
9220+
private:
9221+
void printMethodSignature(const FuncDecl *Method) {
9222+
auto options = PrintOptions::printForDiagnostics(AccessLevel::Private, true);
9223+
StreamPrinter printer(out);
9224+
Method->print(printer, options);
9225+
}
9226+
};
9227+
9228+
static bool swiftifyImpl(SwiftifyInfoPrinter &printer, const FuncDecl *MappedDecl) {
9229+
const clang::Decl *ClangDecl = MappedDecl->getClangDecl();
9230+
auto FuncD = dyn_cast<clang::FunctionDecl>(ClangDecl);
9231+
auto MethodD = dyn_cast<clang::ObjCMethodDecl>(ClangDecl);
9232+
assert(FuncD || MethodD);
91469233

9147-
llvm::SmallString<128> MacroString;
91489234
// We only attach the macro if it will produce an overload. Any __counted_by
91499235
// will produce an overload, since UnsafeBufferPointer is still an improvement
91509236
// over UnsafePointer, but std::span will only produce an overload if it also
91519237
// has lifetime information, since std::span already contains bounds info.
91529238
bool attachMacro = false;
9153-
{
9154-
llvm::raw_svector_ostream out(MacroString);
91559239

9156-
SwiftifyInfoPrinter printer(getClangASTContext(), out);
9157-
bool returnIsStdSpan = printer.registerStdSpanTypeMapping(
9158-
MappedDecl->getResultInterfaceType(), ClangDecl->getReturnType());
9159-
if (auto CAT =
9160-
ClangDecl->getReturnType()->getAs<clang::CountAttributedType>()) {
9161-
printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX);
9162-
attachMacro = true;
9163-
}
9164-
bool returnHasLifetimeInfo = false;
9165-
if (SwiftDeclConverter::getImplicitObjectParamAnnotation<
9166-
clang::LifetimeBoundAttr>(ClangDecl)) {
9167-
printer.printLifetimeboundReturn(SwiftifyInfoPrinter::SELF_PARAM_INDEX,
9168-
true);
9240+
clang::QualType returnType = FuncD ? FuncD->getReturnType() : MethodD->getReturnType();
9241+
bool returnIsStdSpan = printer.registerStdSpanTypeMapping(
9242+
MappedDecl->getResultInterfaceType(), returnType);
9243+
if (auto CAT = returnType->getAs<clang::CountAttributedType>()) {
9244+
printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX);
9245+
attachMacro = true;
9246+
}
9247+
bool returnHasLifetimeInfo = false;
9248+
if (FuncD && SwiftDeclConverter::getImplicitObjectParamAnnotation<clang::LifetimeBoundAttr>(FuncD)) {
9249+
printer.printLifetimeboundReturn(SwiftifyInfoPrinter::SELF_PARAM_INDEX,
9250+
true);
9251+
returnHasLifetimeInfo = true;
9252+
}
9253+
9254+
auto parameters = FuncD ? FuncD->parameters() : MethodD->parameters();
9255+
for (auto [index, clangParam] : llvm::enumerate(parameters)) {
9256+
auto clangParamTy = clangParam->getType();
9257+
auto swiftParam = MappedDecl->getParameters()->get(index);
9258+
bool paramHasBoundsInfo = false;
9259+
if (auto CAT = clangParamTy->getAs<clang::CountAttributedType>()) {
9260+
printer.printCountedBy(CAT, index);
9261+
attachMacro = paramHasBoundsInfo = true;
9262+
}
9263+
bool paramIsStdSpan = printer.registerStdSpanTypeMapping(
9264+
swiftParam->getInterfaceType(), clangParamTy);
9265+
paramHasBoundsInfo |= paramIsStdSpan;
9266+
9267+
bool paramHasLifetimeInfo = false;
9268+
if (clangParam->hasAttr<clang::NoEscapeAttr>()) {
9269+
printer.printNonEscaping(index);
9270+
paramHasLifetimeInfo = true;
9271+
}
9272+
if (clangParam->hasAttr<clang::LifetimeBoundAttr>()) {
9273+
printer.printLifetimeboundReturn(
9274+
index, !paramHasBoundsInfo &&
9275+
swiftParam->getInterfaceType()->isEscapable());
9276+
paramHasLifetimeInfo = true;
91699277
returnHasLifetimeInfo = true;
91709278
}
9171-
for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) {
9172-
auto clangParamTy = clangParam->getType();
9173-
auto swiftParam = MappedDecl->getParameters()->get(index);
9174-
bool paramHasBoundsInfo = false;
9175-
if (auto CAT = clangParamTy->getAs<clang::CountAttributedType>()) {
9176-
printer.printCountedBy(CAT, index);
9177-
attachMacro = paramHasBoundsInfo = true;
9178-
}
9179-
bool paramIsStdSpan = printer.registerStdSpanTypeMapping(
9180-
swiftParam->getInterfaceType(), clangParamTy);
9181-
paramHasBoundsInfo |= paramIsStdSpan;
9182-
9183-
bool paramHasLifetimeInfo = false;
9184-
if (clangParam->hasAttr<clang::NoEscapeAttr>()) {
9185-
printer.printNonEscaping(index);
9186-
paramHasLifetimeInfo = true;
9187-
}
9188-
if (clangParam->hasAttr<clang::LifetimeBoundAttr>()) {
9189-
printer.printLifetimeboundReturn(
9190-
index, !paramHasBoundsInfo &&
9191-
swiftParam->getInterfaceType()->isEscapable());
9192-
paramHasLifetimeInfo = true;
9193-
returnHasLifetimeInfo = true;
9194-
}
9195-
if (paramIsStdSpan && paramHasLifetimeInfo)
9196-
attachMacro = true;
9197-
}
9198-
if (returnIsStdSpan && returnHasLifetimeInfo)
9279+
if (paramIsStdSpan && paramHasLifetimeInfo)
91999280
attachMacro = true;
9281+
}
9282+
if (returnIsStdSpan && returnHasLifetimeInfo)
9283+
attachMacro = true;
9284+
9285+
return attachMacro;
9286+
}
9287+
} // namespace
9288+
9289+
void ClangImporter::Implementation::swiftifyProtocol(
9290+
NominalTypeDecl *MappedDecl) {
9291+
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
9292+
return;
9293+
if (!isa<ProtocolDecl, ClassDecl>(MappedDecl))
9294+
return;
9295+
9296+
llvm::SmallString<128> MacroString;
9297+
{
9298+
llvm::raw_svector_ostream out(MacroString);
9299+
out << "@_SwiftifyImportProtocol";
9300+
9301+
bool hasBoundsAttributes = false;
9302+
SwiftifyProtocolInfoPrinter printer(getClangASTContext(), SwiftContext, out);
9303+
for (Decl *SwiftMember : cast<IterableDeclContext>(MappedDecl)->getAllMembers()) {
9304+
FuncDecl *SwiftDecl = dyn_cast<FuncDecl>(SwiftMember);
9305+
if (!SwiftDecl)
9306+
continue;
9307+
hasBoundsAttributes |=
9308+
printer.printMethod(SwiftDecl);
9309+
}
9310+
9311+
if (!hasBoundsAttributes)
9312+
return;
9313+
printer.printAvailability();
9314+
printer.printTypeMapping();
9315+
}
9316+
9317+
importNontrivialAttribute(MappedDecl, MacroString);
9318+
}
9319+
9320+
void ClangImporter::Implementation::swiftify(
9321+
FuncDecl *MappedDecl) {
9322+
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
9323+
return;
9324+
if (!isa_and_nonnull<clang::FunctionDecl>(MappedDecl->getClangDecl()))
9325+
return;
9326+
9327+
llvm::SmallString<128> MacroString;
9328+
{
9329+
llvm::raw_svector_ostream out(MacroString);
9330+
out << "@_SwiftifyImport";
9331+
9332+
SwiftifyInfoPrinter printer(getClangASTContext(), SwiftContext, out);
9333+
if (!swiftifyImpl(printer, MappedDecl))
9334+
return;
92009335
printer.printTypeMapping();
92019336
}
92029337

9203-
if (attachMacro)
9204-
importNontrivialAttribute(MappedDecl, MacroString);
9338+
importNontrivialAttribute(MappedDecl, MacroString);
92059339
}
92069340

92079341
static bool isUsingMacroName(clang::SourceManager &SM,

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
17551755

17561756
void importSwiftAttrAttributes(Decl *decl);
17571757
void swiftify(FuncDecl *MappedDecl);
1758+
void swiftifyProtocol(NominalTypeDecl *MappedDecl);
17581759

17591760
/// Find the lookup table that corresponds to the given Clang module.
17601761
///

lib/DriverTool/swift_api_digester_main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass {
652652
ProtocolReqAllowlist(std::move(prAllowlist)),
653653
DebugMapping(DebugMapping) {}
654654

655+
655656
void diagnoseMissingAvailable(SDKNodeDecl *D) {
656657
// For extensions of external types, we diagnose individual member's missing
657658
// available attribute instead of the extension itself.

0 commit comments

Comments
 (0)