Skip to content

Commit 96d0450

Browse files
authored
Merge pull request #82940 from tshortli/silgen-custom-availability-domain-query
ClangImporter: Generate and call custom availability domain predicates
2 parents 06c04c5 + 3d722e1 commit 96d0450

17 files changed

+344
-59
lines changed

include/swift/AST/AvailabilityDomain.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class ASTContext;
3434
class CustomAvailabilityDomain;
3535
class Decl;
3636
class DeclContext;
37+
class FuncDecl;
3738
class ModuleDecl;
3839

3940
/// Represents a dimension of availability (e.g. macOS platform or Swift
@@ -336,20 +337,27 @@ class CustomAvailabilityDomain : public llvm::FoldingSetNode {
336337
Kind kind;
337338
ModuleDecl *mod;
338339
Decl *decl;
340+
FuncDecl *predicateFunc;
339341

340342
CustomAvailabilityDomain(Identifier name, Kind kind, ModuleDecl *mod,
341-
Decl *decl);
343+
Decl *decl, FuncDecl *predicateFunc);
342344

343345
public:
344346
static const CustomAvailabilityDomain *get(StringRef name, Kind kind,
345347
ModuleDecl *mod, Decl *decl,
348+
FuncDecl *predicateFunc,
346349
const ASTContext &ctx);
347350

348351
Identifier getName() const { return name; }
349352
Kind getKind() const { return kind; }
350353
ModuleDecl *getModule() const { return mod; }
351354
Decl *getDecl() const { return decl; }
352355

356+
/// Returns the function that should be called at runtime to determine whether
357+
/// the domain is available, or `nullptr` if the domain's availability is not
358+
/// determined at runtime.
359+
FuncDecl *getPredicateFunc() const { return predicateFunc; }
360+
353361
/// Uniquing for `ASTContext`.
354362
static void Profile(llvm::FoldingSetNodeID &ID, Identifier name,
355363
ModuleDecl *mod);

include/swift/AST/ClangModuleLoader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ class ClangModuleLoader : public ModuleLoader {
298298

299299
virtual FuncDecl *getDefaultArgGenerator(const clang::ParmVarDecl *param) = 0;
300300

301+
virtual FuncDecl *
302+
getAvailabilityDomainPredicate(const clang::VarDecl *var) = 0;
303+
301304
virtual std::optional<Type>
302305
importFunctionReturnType(const clang::FunctionDecl *clangDecl,
303306
DeclContext *dc) = 0;

include/swift/ClangImporter/ClangImporter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,8 @@ class ClangImporter final : public ClangModuleLoader {
630630

631631
FuncDecl *getDefaultArgGenerator(const clang::ParmVarDecl *param) override;
632632

633+
FuncDecl *getAvailabilityDomainPredicate(const clang::VarDecl *var) override;
634+
633635
bool isAnnotatedWith(const clang::CXXMethodDecl *method, StringRef attr);
634636

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

lib/AST/ASTContext.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5905,7 +5905,8 @@ const AvailabilityContext::Storage *AvailabilityContext::Storage::get(
59055905

59065906
const CustomAvailabilityDomain *
59075907
CustomAvailabilityDomain::get(StringRef name, Kind kind, ModuleDecl *mod,
5908-
Decl *decl, const ASTContext &ctx) {
5908+
Decl *decl, FuncDecl *predicateFunc,
5909+
const ASTContext &ctx) {
59095910
auto identifier = ctx.getIdentifier(name);
59105911
llvm::FoldingSetNodeID id;
59115912
CustomAvailabilityDomain::Profile(id, identifier, mod);
@@ -5918,8 +5919,8 @@ CustomAvailabilityDomain::get(StringRef name, Kind kind, ModuleDecl *mod,
59185919

59195920
void *mem = ctx.Allocate(sizeof(CustomAvailabilityDomain),
59205921
alignof(CustomAvailabilityDomain));
5921-
auto *newNode =
5922-
::new (mem) CustomAvailabilityDomain(identifier, kind, mod, decl);
5922+
auto *newNode = ::new (mem)
5923+
CustomAvailabilityDomain(identifier, kind, mod, decl, predicateFunc);
59235924
foldingSet.InsertNode(newNode, insertPos);
59245925

59255926
return newNode;

lib/AST/AvailabilityDomain.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/Module.h"
1818
#include "swift/AST/TypeCheckRequests.h"
1919
#include "swift/Basic/Assertions.h"
20+
#include "swift/ClangImporter/ClangImporter.h"
2021
#include "clang/AST/ASTContext.h"
2122
#include "clang/AST/Decl.h"
2223
#include "llvm/ADT/StringSwitch.h"
@@ -42,8 +43,12 @@ customDomainForClangDecl(Decl *decl, const ASTContext &ctx) {
4243
auto *clangDecl = decl->getClangDecl();
4344
ASSERT(clangDecl);
4445

46+
auto *varDecl = dyn_cast<clang::VarDecl>(clangDecl);
47+
if (!varDecl)
48+
return nullptr;
49+
4550
auto featureInfo = clangDecl->getASTContext().getFeatureAvailInfo(
46-
const_cast<clang::Decl *>(clangDecl));
51+
const_cast<clang::VarDecl *>(varDecl));
4752

4853
// Ensure the decl actually represents an availability domain.
4954
if (featureInfo.first.empty())
@@ -52,9 +57,14 @@ customDomainForClangDecl(Decl *decl, const ASTContext &ctx) {
5257
if (featureInfo.second.Kind == clang::FeatureAvailKind::None)
5358
return nullptr;
5459

60+
FuncDecl *predicate = nullptr;
61+
if (featureInfo.second.Kind == clang::FeatureAvailKind::Dynamic)
62+
predicate =
63+
ctx.getClangModuleLoader()->getAvailabilityDomainPredicate(varDecl);
64+
5565
return CustomAvailabilityDomain::get(
5666
featureInfo.first, getCustomDomainKind(featureInfo.second.Kind),
57-
decl->getModuleContext(), decl, ctx);
67+
decl->getModuleContext(), decl, predicate, ctx);
5868
}
5969

6070
std::optional<AvailabilityDomain>
@@ -359,10 +369,14 @@ bool StableAvailabilityDomainComparator::operator()(
359369
}
360370

361371
CustomAvailabilityDomain::CustomAvailabilityDomain(Identifier name, Kind kind,
362-
ModuleDecl *mod, Decl *decl)
363-
: name(name), kind(kind), mod(mod), decl(decl) {
372+
ModuleDecl *mod, Decl *decl,
373+
FuncDecl *predicateFunc)
374+
: name(name), kind(kind), mod(mod), decl(decl),
375+
predicateFunc(predicateFunc) {
364376
ASSERT(!name.empty());
365377
ASSERT(mod);
378+
if (predicateFunc)
379+
ASSERT(kind == Kind::Dynamic);
366380
}
367381

368382
void CustomAvailabilityDomain::Profile(llvm::FoldingSetNodeID &ID,

lib/AST/AvailabilityQuery.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ getOSAvailabilityDeclAndArguments(const AvailabilityQuery &query,
129129

130130
FuncDecl *AvailabilityQuery::getDynamicQueryDeclAndArguments(
131131
llvm::SmallVectorImpl<unsigned> &arguments, ASTContext &ctx) const {
132-
switch (getDomain().getKind()) {
132+
auto domain = getDomain();
133+
switch (domain.getKind()) {
133134
case AvailabilityDomain::Kind::Universal:
134135
case AvailabilityDomain::Kind::SwiftLanguage:
135136
case AvailabilityDomain::Kind::PackageDescription:
@@ -138,7 +139,9 @@ FuncDecl *AvailabilityQuery::getDynamicQueryDeclAndArguments(
138139
case AvailabilityDomain::Kind::Platform:
139140
return getOSAvailabilityDeclAndArguments(*this, arguments, ctx);
140141
case AvailabilityDomain::Kind::Custom:
141-
// FIXME: [availability] Support custom domains.
142-
return nullptr;
142+
auto customDomain = domain.getCustomDomain();
143+
ASSERT(customDomain);
144+
145+
return customDomain->getPredicateFunc();
143146
}
144147
}

lib/ClangImporter/ClangImporter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7646,6 +7646,14 @@ ClangImporter::getDefaultArgGenerator(const clang::ParmVarDecl *param) {
76467646
return nullptr;
76477647
}
76487648

7649+
FuncDecl *
7650+
ClangImporter::getAvailabilityDomainPredicate(const clang::VarDecl *var) {
7651+
auto it = Impl.availabilityDomainPredicates.find(var);
7652+
if (it != Impl.availabilityDomainPredicates.end())
7653+
return it->second;
7654+
return nullptr;
7655+
}
7656+
76497657
SwiftLookupTable *
76507658
ClangImporter::findLookupTable(const clang::Module *clangModule) {
76517659
return Impl.findLookupTable(clangModule);

lib/ClangImporter/ImportDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4675,6 +4675,14 @@ namespace {
46754675
if (correctSwiftName)
46764676
markAsVariant(result, *correctSwiftName);
46774677

4678+
// If the decl represents an availability domain, eagerly synthesize its
4679+
// `if #available` predicate function.
4680+
if (decl->hasAttrs() &&
4681+
llvm::any_of(decl->getAttrs(), [](clang::Attr *attr) {
4682+
return isa<clang::AvailabilityDomainAttr>(attr);
4683+
}))
4684+
(void)synthesizer.makeAvailabilityDomainPredicate(decl);
4685+
46784686
return result;
46794687
}
46804688

lib/ClangImporter/ImporterImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
818818
public:
819819
importer::PlatformAvailability platformAvailability;
820820

821+
/// The synthesized predicate functions for imported `VarDecl`s that represent
822+
/// availability domains.
823+
llvm::DenseMap<const clang::VarDecl *, FuncDecl *>
824+
availabilityDomainPredicates;
825+
821826
private:
822827
/// For importing names. This is initialized by the ClangImporter::create()
823828
/// after having set up a suitable Clang instance.

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2747,3 +2747,107 @@ SwiftDeclSynthesizer::synthesizeStaticFactoryForCXXForeignRef(
27472747

27482748
return synthesizedFactories;
27492749
}
2750+
2751+
static std::pair<BraceStmt *, bool>
2752+
synthesizeAvailabilityDomainPredicateBody(AbstractFunctionDecl *afd,
2753+
void *context) {
2754+
auto clangVarDecl = static_cast<const clang::VarDecl *>(context);
2755+
clang::ASTContext &clangCtx = clangVarDecl->getASTContext();
2756+
auto domainInfo =
2757+
clangCtx.getFeatureAvailInfo(const_cast<clang::VarDecl *>(clangVarDecl));
2758+
ASSERT(domainInfo.second.Call);
2759+
2760+
auto funcDecl = cast<FuncDecl>(afd);
2761+
ASTContext &ctx = funcDecl->getASTContext();
2762+
2763+
// FIXME: The need for an intermediate function to call could be eliminated if
2764+
// Clang provided the predicate function decl directly, rather than a call
2765+
// expression that must be wrapped in a function.
2766+
// Synthesize `return {domain predicate expression}`.
2767+
auto clangHelperReturnStmt = clang::ReturnStmt::Create(
2768+
clangCtx, clang::SourceLocation(), domainInfo.second.Call, nullptr);
2769+
2770+
// Synthesize `int __XYZ_isAvailable() { return {predicate expr}; }`.
2771+
auto clangDeclName = clang::DeclarationName(
2772+
&clangCtx.Idents.get("__" + domainInfo.first.str() + "_isAvailable"));
2773+
auto clangDeclContext = clangCtx.getTranslationUnitDecl();
2774+
clang::QualType funcTy =
2775+
clangCtx.getFunctionType(domainInfo.second.Call->getType(), {},
2776+
clang::FunctionProtoType::ExtProtoInfo());
2777+
2778+
auto clangHelperFuncDecl = clang::FunctionDecl::Create(
2779+
clangCtx, clangDeclContext, clang::SourceLocation(),
2780+
clang::SourceLocation(), clangDeclName, funcTy,
2781+
clangCtx.getTrivialTypeSourceInfo(funcTy),
2782+
clang::StorageClass::SC_Static);
2783+
clangHelperFuncDecl->setImplicit();
2784+
clangHelperFuncDecl->setImplicitlyInline();
2785+
clangHelperFuncDecl->setBody(clangHelperReturnStmt);
2786+
2787+
// Import `func __XYZ_isAvailable() -> Bool` into Swift.
2788+
auto helperFuncDecl = dyn_cast_or_null<FuncDecl>(
2789+
ctx.getClangModuleLoader()->importDeclDirectly(clangHelperFuncDecl));
2790+
if (!helperFuncDecl)
2791+
return {nullptr, /*isTypeChecked=*/true};
2792+
2793+
auto helperFuncRef = new (ctx) DeclRefExpr(ConcreteDeclRef(helperFuncDecl),
2794+
DeclNameLoc(), /*Implicit=*/true);
2795+
helperFuncRef->setType(helperFuncDecl->getInterfaceType());
2796+
2797+
// Synthesize `__XYZ_isAvailable()`.
2798+
auto helperCall = CallExpr::createImplicit(
2799+
ctx, helperFuncRef, ArgumentList::createImplicit(ctx, {}));
2800+
helperCall->setType(helperFuncDecl->getResultInterfaceType());
2801+
helperCall->setThrows(nullptr);
2802+
2803+
// Synthesize `__XYZ_isAvailable()._value`.
2804+
auto *memberRef =
2805+
UnresolvedDotExpr::createImplicit(ctx, helperCall, ctx.Id_value_);
2806+
2807+
// Synthesize `return __XYZ_isAvailable()._value`.
2808+
auto *returnStmt = ReturnStmt::createImplicit(ctx, memberRef);
2809+
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
2810+
/*implicit=*/true);
2811+
2812+
return {body, /*isTypeChecked=*/false};
2813+
}
2814+
2815+
FuncDecl *SwiftDeclSynthesizer::makeAvailabilityDomainPredicate(
2816+
const clang::VarDecl *var) {
2817+
ASTContext &ctx = ImporterImpl.SwiftContext;
2818+
clang::ASTContext &clangCtx = var->getASTContext();
2819+
auto featureInfo =
2820+
clangCtx.getFeatureAvailInfo(const_cast<clang::VarDecl *>(var));
2821+
2822+
// If the decl doesn't represent and availability domain, skip it.
2823+
if (featureInfo.first.empty())
2824+
return nullptr;
2825+
2826+
// Only dynamic availability domains require a predicate function.
2827+
if (featureInfo.second.Kind != clang::FeatureAvailKind::Dynamic)
2828+
return nullptr;
2829+
2830+
if (!featureInfo.second.Call)
2831+
return nullptr;
2832+
2833+
// Synthesize `func __swift_XYZ_isAvailable() -> Builtin.Int1 { ... }`.
2834+
std::string s;
2835+
llvm::raw_string_ostream os(s);
2836+
os << "__swift_" << featureInfo.first << "_isAvailable";
2837+
DeclName funcName(ctx, DeclBaseName(ctx.getIdentifier(s)),
2838+
ParameterList::createEmpty(ctx));
2839+
2840+
auto funcDecl = FuncDecl::createImplicit(
2841+
ctx, StaticSpellingKind::None, funcName, SourceLoc(), /*Async=*/false,
2842+
/*Throws=*/false, Type(), {}, ParameterList::createEmpty(ctx),
2843+
BuiltinIntegerType::get(1, ctx), ImporterImpl.ImportedHeaderUnit);
2844+
funcDecl->setBodySynthesizer(synthesizeAvailabilityDomainPredicateBody,
2845+
(void *)var);
2846+
funcDecl->setAccess(AccessLevel::Public);
2847+
funcDecl->getAttrs().add(new (ctx)
2848+
AlwaysEmitIntoClientAttr(/*IsImplicit=*/true));
2849+
2850+
ImporterImpl.availabilityDomainPredicates[var] = funcDecl;
2851+
2852+
return funcDecl;
2853+
}

0 commit comments

Comments
 (0)