Skip to content

Commit e864a54

Browse files
committed
[Macros] Support user-defined macros as compiler plugins
Allow user-defined macros to be loaded from dynamic libraries and evaluated. - Introduce a _CompilerPluginSupport module installed into the toolchain. Its `_CompilerPlugin` protocol acts as a stable interface between the compiler and user-defined macros. - Introduce a `-compiler-plugin-library-path <path>` attribute which allows users to specify dynamic libraries to be loaded into the compiler. A macro library must declare a public top-level computed property `public var allMacros: [Any.Type]` and be compiled to a dynamic library. The compiler will call the getter of this property to obtain and regsiter all macros. Known issues: - We current do not have a way to strip out unnecessary symbols from the plugin dylib, aka. produce a plugin library that does not contain SwiftSyntax symbols that will collide with the compiler itself. - `MacroExpansionExpr`'s type is hard-coded as `(Int, String)`. It should instead be specified by the macro via protocol requirements such as `signature` and `genericSignature`. We need more protocol requirements in `_CompilerPlugin` to handle this. - `dlopen` is not secure and is only for prototyping use here.
1 parent ae4ad5d commit e864a54

34 files changed

+847
-31
lines changed

include/swift/AST/ASTContext.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "llvm/ADT/TinyPtrVector.h"
4545
#include "llvm/Support/Allocator.h"
4646
#include "llvm/Support/DataTypes.h"
47+
#include "llvm/Support/DynamicLibrary.h"
4748
#include <functional>
4849
#include <memory>
4950
#include <utility>
@@ -132,6 +133,7 @@ namespace swift {
132133
class IndexSubset;
133134
struct SILAutoDiffDerivativeFunctionKey;
134135
struct InterfaceSubContextDelegate;
136+
class CompilerPlugin;
135137

136138
enum class KnownProtocolKind : uint8_t;
137139

@@ -347,6 +349,12 @@ class ASTContext final {
347349
llvm::SmallPtrSet<DerivativeAttr *, 1>>
348350
DerivativeAttrs;
349351

352+
/// Cache of compiler plugins keyed by their name.
353+
llvm::StringMap<CompilerPlugin> LoadedPlugins;
354+
355+
/// Cache of loaded symbols.
356+
llvm::StringMap<void *> LoadedSymbols;
357+
350358
private:
351359
/// The current generation number, which reflects the number of
352360
/// times that external modules have been loaded.
@@ -1435,6 +1443,14 @@ class ASTContext final {
14351443
/// The declared interface type of Builtin.TheTupleType.
14361444
BuiltinTupleType *getBuiltinTupleType();
14371445

1446+
/// Finds the loaded compiler plugin given its name.
1447+
CompilerPlugin *getLoadedPlugin(StringRef name);
1448+
1449+
/// Finds the address of the given symbol. If `library` is non-null, search
1450+
/// within the library.
1451+
void *getAddressOfSymbol(StringRef name,
1452+
llvm::sys::DynamicLibrary *libraryHint = nullptr);
1453+
14381454
private:
14391455
friend Decl;
14401456

@@ -1447,6 +1463,8 @@ class ASTContext final {
14471463
Optional<StringRef> getBriefComment(const Decl *D);
14481464
void setBriefComment(const Decl *D, StringRef Comment);
14491465

1466+
void loadCompilerPlugins();
1467+
14501468
friend TypeBase;
14511469
friend ArchetypeType;
14521470
friend OpaqueTypeDecl;

include/swift/AST/CompilerPlugin.h

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//===--- CompilerPlugin.h - Compiler Plugin Support -------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines supporting data structures for compiler plugins.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/AST/Type.h"
18+
#include "swift/Basic/LLVM.h"
19+
#include "llvm/Support/DynamicLibrary.h"
20+
21+
#ifndef SWIFT_AST_COMPILER_PLUGIN_H
22+
#define SWIFT_AST_COMPILER_PLUGIN_H
23+
24+
namespace swift {
25+
26+
class ASTContext;
27+
28+
// Must be modified together with `_CompilerPluginKind` in
29+
// stdlib/toolchain/CompilerPluginSupport.swift.
30+
enum class CompilerPluginKind: uint32_t {
31+
ExpressionMacro,
32+
};
33+
34+
/// A compiler plugin that corresponds to a dynamically loaded Swift type that
35+
/// conforms to the `_CompilerPluginSupport._CompilerPlugin` protocol.
36+
class CompilerPlugin {
37+
friend class ASTContext;
38+
39+
// Must be modified together with `_CompilerPlugin` in
40+
// stdlib/toolchain/CompilerPluginSupport.swift.
41+
enum class WitnessTableEntry: unsigned {
42+
ConformanceDescriptor = 0,
43+
// static func _name() -> (UnsafePointer<UInt8>, count: Int8)
44+
Name = 1,
45+
// static func _kind() -> _CompilerPluginKind
46+
Kind = 2,
47+
// static func _rewrite(...) -> (UnsafePointer<UInt8>?, count: Int)
48+
Rewrite = 3,
49+
};
50+
51+
/// The plugin type metadata.
52+
const void *metadata;
53+
/// The parent dynamic library containing the plugin.
54+
llvm::sys::DynamicLibrary parentLibrary;
55+
/// The witness table proving that the plugin conforms to `_CompilerPlugin`.
56+
const void *witnessTable;
57+
/// The plugin name, aka. result of the `_name()` method.
58+
StringRef name;
59+
/// The plugin's kind, aka. result of the `_kind()` method.
60+
CompilerPluginKind kind;
61+
62+
template<typename Func>
63+
const Func *getWitnessMethodUnsafe(WitnessTableEntry entry) const {
64+
return reinterpret_cast<const Func *const *>(witnessTable)[(unsigned)entry];
65+
}
66+
67+
protected:
68+
CompilerPlugin(const void *metadata, llvm::sys::DynamicLibrary parentLibrary,
69+
ASTContext &ctx);
70+
71+
private:
72+
/// Invoke the `_name` method. The caller assumes ownership of the result
73+
/// string buffer.
74+
StringRef invokeName() const;
75+
76+
/// Invoke the `_kind` method.
77+
CompilerPluginKind invokeKind() const;
78+
79+
public:
80+
/// Invoke the `_rewrite` method. Invoke the `_name` method. The caller
81+
/// assumes ownership of the result string buffer.
82+
Optional<StringRef> invokeRewrite(StringRef targetModuleName,
83+
StringRef filePath,
84+
StringRef sourceFileText,
85+
CharSourceRange range,
86+
ASTContext &ctx) const;
87+
88+
StringRef getName() const {
89+
return name;
90+
}
91+
92+
CompilerPluginKind getKind() const {
93+
return kind;
94+
}
95+
};
96+
97+
} // end namespace swift
98+
99+
#endif // SWIFT_AST_COMPILER_PLUGIN_H

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,12 @@ REMARK(warning_in_access_notes_file,none,
472472
"ignored invalid content in access notes file: %0",
473473
(StringRef))
474474

475+
WARNING(compiler_plugin_not_loaded,none,
476+
"compiler plugin not loaded: %0; loader error: %1", (StringRef, StringRef))
477+
WARNING(compiler_plugin_missing_macro_declaration,none,
478+
"compiler plugin module '%0' (in %1) is missing a top-level computed "
479+
"property 'public var %2: [Any.Type]' to declare all macros; "
480+
"undeclared macros will be ignored", (StringRef, StringRef, StringRef))
475481

476482
#define UNDEFINE_DIAGNOSTIC_MACROS
477483
#include "DefineDiagnosticMacros.h"

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6670,8 +6670,14 @@ ERROR(experimental_no_metadata_feature_can_only_be_used_when_enabled,
66706670
//------------------------------------------------------------------------------
66716671
// MARK: Macros
66726672
//------------------------------------------------------------------------------
6673+
66736674
ERROR(expected_macro_expansion_expr,PointsToFirstBadToken,
66746675
"expected macro expansion to produce an expression", ())
6676+
ERROR(macro_expansion_missing_feature_flag,PointsToFirstBadToken,
6677+
"macro expansion requires experimental feature 'Macros'", ())
6678+
ERROR(macro_undefined,PointsToFirstBadToken,
6679+
"macro '%0' is undefined; use `-compiler-plugin-library-path` to specify "
6680+
"dynamic libraries that contain this macro", (StringRef))
66756681

66766682
#define UNDEFINE_DIAGNOSTIC_MACROS
66776683
#include "DefineDiagnosticMacros.h"

include/swift/AST/KnownIdentifiers.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ IDENTIFIER(unsafe)
331331
// The singleton instance of TupleTypeDecl in the Builtin module
332332
IDENTIFIER(TheTupleType)
333333

334+
// Macros / compiler plugins
335+
IDENTIFIER_(CompilerPluginSupport)
336+
334337
#undef IDENTIFIER
335338
#undef IDENTIFIER_
336339
#undef IDENTIFIER_WITH_NAME

include/swift/AST/KnownProtocols.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ PROTOCOL(AsyncIteratorProtocol)
113113

114114
PROTOCOL(FloatingPoint)
115115

116+
// Compiler Plugins
117+
PROTOCOL_(CompilerPlugin)
118+
116119
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)
117120
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByBooleanLiteral, "BooleanLiteralType", true)
118121
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByDictionaryLiteral, "Dictionary", false)

include/swift/AST/SearchPathOptions.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum class ModuleSearchPathKind {
3434
Framework,
3535
DarwinImplicitFramework,
3636
RuntimeLibrary,
37+
CompilerPlugin,
3738
};
3839

3940
/// A single module search path that can come from different sources, e.g.
@@ -230,6 +231,9 @@ class SearchPathOptions {
230231
/// \c ModuleSearchPath.
231232
std::vector<std::string> DarwinImplicitFrameworkSearchPaths;
232233

234+
/// Compiler plugin library search paths.
235+
std::vector<std::string> CompilerPluginLibraryPaths;
236+
233237
/// Add a single import search path. Must only be called from
234238
/// \c ASTContext::addSearchPath.
235239
void addImportSearchPath(StringRef Path, llvm::vfs::FileSystem *FS) {
@@ -239,6 +243,14 @@ class SearchPathOptions {
239243
ImportSearchPaths.size() - 1);
240244
}
241245

246+
void addCompilerPluginLibraryPath(StringRef Path, llvm::vfs::FileSystem *FS) {
247+
CompilerPluginLibraryPaths.push_back(Path.str());
248+
Lookup.searchPathAdded(FS, CompilerPluginLibraryPaths.back(),
249+
ModuleSearchPathKind::CompilerPlugin,
250+
/*isSystem=*/false,
251+
CompilerPluginLibraryPaths.size() - 1);
252+
}
253+
242254
/// Add a single framework search path. Must only be called from
243255
/// \c ASTContext::addSearchPath.
244256
void addFrameworkSearchPath(FrameworkSearchPath NewPath,
@@ -302,6 +314,16 @@ class SearchPathOptions {
302314
Lookup.searchPathsDidChange();
303315
}
304316

317+
void setCompilerPluginLibraryPaths(
318+
std::vector<std::string> NewCompilerPluginLibraryPaths) {
319+
CompilerPluginLibraryPaths = NewCompilerPluginLibraryPaths;
320+
Lookup.searchPathsDidChange();
321+
}
322+
323+
ArrayRef<std::string> getCompilerPluginLibraryPaths() const {
324+
return CompilerPluginLibraryPaths;
325+
}
326+
305327
/// Path(s) to virtual filesystem overlay YAML files.
306328
std::vector<std::string> VFSOverlayFiles;
307329

include/swift/Frontend/Frontend.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,14 @@ class CompilerInvocation {
192192
return SearchPathOpts.getFrameworkSearchPaths();
193193
}
194194

195+
void setCompilerPluginLibraryPaths(const std::vector<std::string> &Paths) {
196+
SearchPathOpts.setCompilerPluginLibraryPaths(Paths);
197+
}
198+
199+
ArrayRef<std::string> getCompilerPluginLibraryPaths() {
200+
return SearchPathOpts.getCompilerPluginLibraryPaths();
201+
}
202+
195203
void setExtraClangArgs(const std::vector<std::string> &Args) {
196204
ClangImporterOpts.ExtraArgs = Args;
197205
}

include/swift/Option/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,4 +1695,11 @@ def nostartfiles:
16951695

16961696
// END ONLY SUPPORTED IN NEW DRIVER
16971697

1698+
def compiler_plugin_library_path:
1699+
Separate<["-"], "compiler-plugin-library-path">,
1700+
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
1701+
HelpText<"Path to a dynamic library containing compiler plugins such as "
1702+
"macros">,
1703+
MetaVarName<"<path>">;
1704+
16981705
include "FrontendOptions.td"

lib/AST/ASTBridging.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "swift/AST/ASTBridging.h"
1414

15+
#include "swift/AST/ASTContext.h"
1516
#include "swift/AST/DiagnosticEngine.h"
1617
#include "swift/Basic/BridgingUtils.h"
1718

lib/AST/ASTContext.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
#include "ForeignRepresentationInfo.h"
2020
#include "SubstitutionMapStorage.h"
2121
#include "swift/AST/ClangModuleLoader.h"
22+
#include "swift/AST/CompilerPlugin.h"
2223
#include "swift/AST/ConcreteDeclRef.h"
2324
#include "swift/AST/DiagnosticEngine.h"
2425
#include "swift/AST/DiagnosticsSema.h"
26+
#include "swift/AST/DiagnosticsFrontend.h"
2527
#include "swift/AST/DistributedDecl.h"
2628
#include "swift/AST/ExistentialLayout.h"
2729
#include "swift/AST/ExtInfo.h"
@@ -64,6 +66,7 @@
6466
#include "llvm/ADT/StringMap.h"
6567
#include "llvm/ADT/StringSwitch.h"
6668
#include "llvm/ADT/STLExtras.h"
69+
#include "llvm/Config/config.h"
6770
#include "llvm/IR/LLVMContext.h"
6871
#include "llvm/Support/Allocator.h"
6972
#include "llvm/Support/Compiler.h"
@@ -96,7 +99,8 @@ llvm::StringRef swift::getProtocolName(KnownProtocolKind kind) {
9699
namespace {
97100
enum class SearchPathKind : uint8_t {
98101
Import = 1 << 0,
99-
Framework = 1 << 1
102+
Framework = 1 << 1,
103+
CompilerPlugin = 1 << 2
100104
};
101105
} // end anonymous namespace
102106

@@ -680,10 +684,14 @@ ASTContext::ASTContext(
680684
getImpl().SearchPathsSet[path] |= SearchPathKind::Import;
681685
for (const auto &framepath : SearchPathOpts.getFrameworkSearchPaths())
682686
getImpl().SearchPathsSet[framepath.Path] |= SearchPathKind::Framework;
687+
for (StringRef path : SearchPathOpts.getCompilerPluginLibraryPaths())
688+
getImpl().SearchPathsSet[path] |= SearchPathKind::CompilerPlugin;
683689

684690
// Register any request-evaluator functions available at the AST layer.
685691
registerAccessRequestFunctions(evaluator);
686692
registerNameLookupRequestFunctions(evaluator);
693+
694+
loadCompilerPlugins();
687695
}
688696

689697
ASTContext::~ASTContext() {
@@ -1117,6 +1125,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
11171125
case KnownProtocolKind::UnsafeCxxInputIterator:
11181126
M = getLoadedModule(Id_Cxx);
11191127
break;
1128+
case KnownProtocolKind::CompilerPlugin:
1129+
M = getLoadedModule(Id_CompilerPluginSupport);
1130+
break;
11201131
default:
11211132
M = getStdlibModule();
11221133
break;
@@ -6046,3 +6057,24 @@ BuiltinTupleType *ASTContext::getBuiltinTupleType() {
60466057

60476058
return result;
60486059
}
6060+
6061+
CompilerPlugin *ASTContext::getLoadedPlugin(StringRef name) {
6062+
auto lookup = LoadedPlugins.find(name);
6063+
if (lookup == LoadedPlugins.end())
6064+
return nullptr;
6065+
return &lookup->second;
6066+
}
6067+
6068+
void *ASTContext::getAddressOfSymbol(StringRef name,
6069+
llvm::sys::DynamicLibrary *libraryHint) {
6070+
auto lookup = LoadedSymbols.try_emplace(name, nullptr);
6071+
void *&address = lookup.first->getValue();
6072+
if (lookup.second) {
6073+
if (libraryHint && libraryHint->isValid())
6074+
address = libraryHint->getAddressOfSymbol(name.data());
6075+
else
6076+
address = llvm::sys::DynamicLibrary::
6077+
SearchForAddressOfSymbol(name.data());
6078+
}
6079+
return address;
6080+
}

lib/AST/ASTWalker.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,8 +1247,9 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
12471247
}
12481248

12491249
Expr *visitMacroExpansionExpr(MacroExpansionExpr *E) {
1250-
auto *macro = doIt(E->getMacro());
1251-
if (!macro) return nullptr;
1250+
// Don't want the callee expr yet as it will stay as a UDRE for now.
1251+
// auto *macro = doIt(E->getMacro());
1252+
// if (!macro) return nullptr;
12521253
Expr *rewritten = nullptr;
12531254
if (E->getRewritten()) {
12541255
rewritten = doIt(E->getRewritten());
@@ -1259,7 +1260,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
12591260
args = doIt(E->getArgs());
12601261
if (!args) return nullptr;
12611262
}
1262-
E->setMacro(macro);
1263+
// E->setMacro(macro);
12631264
E->setRewritten(rewritten);
12641265
E->setArgs(args);
12651266
return E;

0 commit comments

Comments
 (0)