Skip to content

Commit fdce2b7

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 `-load-plugin-library <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, i.e. 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 052a77e commit fdce2b7

33 files changed

+896
-27
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 `libraryHint` is non-null,
1450+
/// search 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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6696,8 +6696,12 @@ ERROR(experimental_no_metadata_feature_can_only_be_used_when_enabled,
66966696
//------------------------------------------------------------------------------
66976697
// MARK: Macros
66986698
//------------------------------------------------------------------------------
6699+
66996700
ERROR(expected_macro_expansion_expr,PointsToFirstBadToken,
67006701
"expected macro expansion to produce an expression", ())
6702+
ERROR(macro_undefined,PointsToFirstBadToken,
6703+
"macro '%0' is undefined; use `-load-plugin-library` to specify dynamic "
6704+
"libraries that contain this macro", (StringRef))
67016705

67026706
#define UNDEFINE_DIAGNOSTIC_MACROS
67036707
#include "DefineDiagnosticMacros.h"

include/swift/AST/KnownIdentifiers.def

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

336+
// Macros / compiler plugins
337+
IDENTIFIER_(CompilerPluginSupport)
338+
336339
#undef IDENTIFIER
337340
#undef IDENTIFIER_
338341
#undef IDENTIFIER_WITH_NAME

include/swift/AST/KnownProtocols.def

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

115115
PROTOCOL(FloatingPoint)
116116

117+
// Compiler Plugins
118+
PROTOCOL_(CompilerPlugin)
119+
117120
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)
118121
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByBooleanLiteral, "BooleanLiteralType", true)
119122
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 load_plugin_library:
1699+
Separate<["-"], "load-plugin-library">,
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/ASTContext.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
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"
@@ -96,7 +97,8 @@ llvm::StringRef swift::getProtocolName(KnownProtocolKind kind) {
9697
namespace {
9798
enum class SearchPathKind : uint8_t {
9899
Import = 1 << 0,
99-
Framework = 1 << 1
100+
Framework = 1 << 1,
101+
CompilerPlugin = 1 << 2
100102
};
101103
} // end anonymous namespace
102104

@@ -680,10 +682,14 @@ ASTContext::ASTContext(
680682
getImpl().SearchPathsSet[path] |= SearchPathKind::Import;
681683
for (const auto &framepath : SearchPathOpts.getFrameworkSearchPaths())
682684
getImpl().SearchPathsSet[framepath.Path] |= SearchPathKind::Framework;
685+
for (StringRef path : SearchPathOpts.getCompilerPluginLibraryPaths())
686+
getImpl().SearchPathsSet[path] |= SearchPathKind::CompilerPlugin;
683687

684688
// Register any request-evaluator functions available at the AST layer.
685689
registerAccessRequestFunctions(evaluator);
686690
registerNameLookupRequestFunctions(evaluator);
691+
692+
loadCompilerPlugins();
687693
}
688694

689695
ASTContext::~ASTContext() {
@@ -1118,6 +1124,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
11181124
case KnownProtocolKind::UnsafeCxxRandomAccessIterator:
11191125
M = getLoadedModule(Id_Cxx);
11201126
break;
1127+
case KnownProtocolKind::CompilerPlugin:
1128+
M = getLoadedModule(Id_CompilerPluginSupport);
1129+
break;
11211130
default:
11221131
M = getStdlibModule();
11231132
break;
@@ -6032,3 +6041,24 @@ BuiltinTupleType *ASTContext::getBuiltinTupleType() {
60326041

60336042
return result;
60346043
}
6044+
6045+
CompilerPlugin *ASTContext::getLoadedPlugin(StringRef name) {
6046+
auto lookup = LoadedPlugins.find(name);
6047+
if (lookup == LoadedPlugins.end())
6048+
return nullptr;
6049+
return &lookup->second;
6050+
}
6051+
6052+
void *ASTContext::getAddressOfSymbol(StringRef name,
6053+
llvm::sys::DynamicLibrary *libraryHint) {
6054+
auto lookup = LoadedSymbols.try_emplace(name, nullptr);
6055+
void *&address = lookup.first->getValue();
6056+
if (lookup.second) {
6057+
if (libraryHint && libraryHint->isValid())
6058+
address = libraryHint->getAddressOfSymbol(name.data());
6059+
else
6060+
address = llvm::sys::DynamicLibrary::
6061+
SearchForAddressOfSymbol(name.data());
6062+
}
6063+
return address;
6064+
}

lib/AST/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ add_swift_host_library(swiftAST STATIC
3737
CaptureInfo.cpp
3838
ClangSwiftTypeCorrespondence.cpp
3939
ClangTypeConverter.cpp
40+
CompilerPlugin.cpp
4041
ConcreteDeclRef.cpp
4142
ConformanceLookupTable.cpp
4243
Decl.cpp
@@ -156,6 +157,13 @@ if(NOT SWIFT_BUILD_ONLY_SYNTAXPARSERLIB)
156157
clangBasic)
157158
endif()
158159

160+
if(SWIFT_SWIFT_PARSER)
161+
target_compile_definitions(swiftAST
162+
PRIVATE
163+
SWIFT_SWIFT_PARSER
164+
)
165+
endif()
166+
159167
target_link_libraries(swiftAST
160168
PUBLIC swiftBasic
161169
PRIVATE swiftSyntax)

0 commit comments

Comments
 (0)