Skip to content

[Macros] Support user-defined macros as compiler plugins #61734

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/DynamicLibrary.h"
#include <functional>
#include <memory>
#include <utility>
Expand Down Expand Up @@ -132,6 +133,7 @@ namespace swift {
class IndexSubset;
struct SILAutoDiffDerivativeFunctionKey;
struct InterfaceSubContextDelegate;
class CompilerPlugin;

enum class KnownProtocolKind : uint8_t;

Expand Down Expand Up @@ -347,6 +349,12 @@ class ASTContext final {
llvm::SmallPtrSet<DerivativeAttr *, 1>>
DerivativeAttrs;

/// Cache of compiler plugins keyed by their name.
llvm::StringMap<CompilerPlugin> LoadedPlugins;

/// Cache of loaded symbols.
llvm::StringMap<void *> LoadedSymbols;

private:
/// The current generation number, which reflects the number of
/// times that external modules have been loaded.
Expand Down Expand Up @@ -1435,6 +1443,14 @@ class ASTContext final {
/// The declared interface type of Builtin.TheTupleType.
BuiltinTupleType *getBuiltinTupleType();

/// Finds the loaded compiler plugin given its name.
CompilerPlugin *getLoadedPlugin(StringRef name);

/// Finds the address of the given symbol. If `libraryHint` is non-null,
/// search within the library.
void *getAddressOfSymbol(StringRef name,
llvm::sys::DynamicLibrary *libraryHint = nullptr);

private:
friend Decl;

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

void loadCompilerPlugins();

friend TypeBase;
friend ArchetypeType;
friend OpaqueTypeDecl;
Expand Down
100 changes: 100 additions & 0 deletions include/swift/AST/CompilerPlugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//===--- CompilerPlugin.h - Compiler Plugin Support -------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines supporting data structures for compiler plugins.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/Type.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/StringExtras.h"
#include "llvm/Support/DynamicLibrary.h"

#ifndef SWIFT_AST_COMPILER_PLUGIN_H
#define SWIFT_AST_COMPILER_PLUGIN_H

namespace swift {

class ASTContext;

/// A compiler plugin that corresponds to a dynamically loaded Swift type that
/// conforms to the `_CompilerPluginSupport._CompilerPlugin` protocol.
class CompilerPlugin {
friend class ASTContext;

public:
// Must be modified together with `_CompilerPluginKind` in
// stdlib/toolchain/CompilerPluginSupport.swift.
enum class Kind: uint32_t {
ExpressionMacro,
};

private:
// Must be modified together with `_CompilerPlugin` in
// stdlib/toolchain/CompilerPluginSupport.swift.
enum class WitnessTableEntry: unsigned {
ConformanceDescriptor = 0,
// static func _name() -> (UnsafePointer<UInt8>, count: Int8)
Name = 1,
// static func _kind() -> _CompilerPluginKind
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the _CompilerPlugin protocol is resilient, we don't have to hard-code these. Rather, we could look up the requirement symbols and do the address calculation to find the entries dynamically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about that, but most of the protocol layout utilities are down in IRGen. Here we could look up a requirement but it doesn't provide the offset info. We could go though all requirements up front and build up an offset table ahead of time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind = 2,
// static func _rewrite(...) -> (UnsafePointer<UInt8>?, count: Int)
Rewrite = 3,
};

/// The plugin type metadata.
const void *metadata;
/// The parent dynamic library containing the plugin.
llvm::sys::DynamicLibrary parentLibrary;
/// The witness table proving that the plugin conforms to `_CompilerPlugin`.
const void *witnessTable;
/// The plugin name, aka. result of the `_name()` method.
StringRef name;
/// The plugin's kind, aka. result of the `_kind()` method.
Kind kind;

template<typename Func>
const Func *getWitnessMethodUnsafe(WitnessTableEntry entry) const {
return reinterpret_cast<const Func *const *>(witnessTable)[(unsigned)entry];
}

protected:
CompilerPlugin(const void *metadata, llvm::sys::DynamicLibrary parentLibrary,
ASTContext &ctx);

private:
/// Invoke the `_name` method. The caller assumes ownership of the result
/// string buffer.
StringRef invokeName() const;

/// Invoke the `_kind` method.
Kind invokeKind() const;

public:
/// Invoke the `_rewrite` method. Invoke the `_name` method. The caller
/// assumes ownership of the result string buffer.
Optional<NullTerminatedStringRef> invokeRewrite(
StringRef targetModuleName, StringRef filePath, StringRef sourceFileText,
CharSourceRange range, ASTContext &ctx) const;

StringRef getName() const {
return name;
}

Kind getKind() const {
return kind;
}
};

} // end namespace swift

#endif // SWIFT_AST_COMPILER_PLUGIN_H
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,12 @@ REMARK(warning_in_access_notes_file,none,
"ignored invalid content in access notes file: %0",
(StringRef))

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

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6696,8 +6696,12 @@ ERROR(experimental_no_metadata_feature_can_only_be_used_when_enabled,
//------------------------------------------------------------------------------
// MARK: Macros
//------------------------------------------------------------------------------

ERROR(expected_macro_expansion_expr,PointsToFirstBadToken,
"expected macro expansion to produce an expression", ())
ERROR(macro_undefined,PointsToFirstBadToken,
"macro '%0' is undefined; use `-load-plugin-library` to specify dynamic "
"libraries that contain this macro", (StringRef))

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
3 changes: 3 additions & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ IDENTIFIER(unsafe)
// The singleton instance of TupleTypeDecl in the Builtin module
IDENTIFIER(TheTupleType)

// Macros / compiler plugins
IDENTIFIER_(CompilerPluginSupport)

#undef IDENTIFIER
#undef IDENTIFIER_
#undef IDENTIFIER_WITH_NAME
3 changes: 3 additions & 0 deletions include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ PROTOCOL(AsyncIteratorProtocol)

PROTOCOL(FloatingPoint)

// Compiler Plugins
PROTOCOL_(CompilerPlugin)

EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByBooleanLiteral, "BooleanLiteralType", true)
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByDictionaryLiteral, "Dictionary", false)
Expand Down
22 changes: 22 additions & 0 deletions include/swift/AST/SearchPathOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ enum class ModuleSearchPathKind {
Framework,
DarwinImplicitFramework,
RuntimeLibrary,
CompilerPlugin,
};

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

/// Compiler plugin library search paths.
std::vector<std::string> CompilerPluginLibraryPaths;

/// Add a single import search path. Must only be called from
/// \c ASTContext::addSearchPath.
void addImportSearchPath(StringRef Path, llvm::vfs::FileSystem *FS) {
Expand All @@ -239,6 +243,14 @@ class SearchPathOptions {
ImportSearchPaths.size() - 1);
}

void addCompilerPluginLibraryPath(StringRef Path, llvm::vfs::FileSystem *FS) {
CompilerPluginLibraryPaths.push_back(Path.str());
Lookup.searchPathAdded(FS, CompilerPluginLibraryPaths.back(),
ModuleSearchPathKind::CompilerPlugin,
/*isSystem=*/false,
CompilerPluginLibraryPaths.size() - 1);
}

/// Add a single framework search path. Must only be called from
/// \c ASTContext::addSearchPath.
void addFrameworkSearchPath(FrameworkSearchPath NewPath,
Expand Down Expand Up @@ -302,6 +314,16 @@ class SearchPathOptions {
Lookup.searchPathsDidChange();
}

void setCompilerPluginLibraryPaths(
std::vector<std::string> NewCompilerPluginLibraryPaths) {
CompilerPluginLibraryPaths = NewCompilerPluginLibraryPaths;
Lookup.searchPathsDidChange();
}

ArrayRef<std::string> getCompilerPluginLibraryPaths() const {
return CompilerPluginLibraryPaths;
}

/// Path(s) to virtual filesystem overlay YAML files.
std::vector<std::string> VFSOverlayFiles;

Expand Down
8 changes: 8 additions & 0 deletions include/swift/Frontend/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ class CompilerInvocation {
return SearchPathOpts.getFrameworkSearchPaths();
}

void setCompilerPluginLibraryPaths(const std::vector<std::string> &Paths) {
SearchPathOpts.setCompilerPluginLibraryPaths(Paths);
}

ArrayRef<std::string> getCompilerPluginLibraryPaths() {
return SearchPathOpts.getCompilerPluginLibraryPaths();
}

void setExtraClangArgs(const std::vector<std::string> &Args) {
ClangImporterOpts.ExtraArgs = Args;
}
Expand Down
7 changes: 7 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1695,4 +1695,11 @@ def nostartfiles:

// END ONLY SUPPORTED IN NEW DRIVER

def load_plugin_library:
Separate<["-"], "load-plugin-library">,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
HelpText<"Path to a dynamic library containing compiler plugins such as "
"macros">,
MetaVarName<"<path>">;

include "FrontendOptions.td"
32 changes: 31 additions & 1 deletion lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "ForeignRepresentationInfo.h"
#include "SubstitutionMapStorage.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/CompilerPlugin.h"
#include "swift/AST/ConcreteDeclRef.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsSema.h"
Expand Down Expand Up @@ -96,7 +97,8 @@ llvm::StringRef swift::getProtocolName(KnownProtocolKind kind) {
namespace {
enum class SearchPathKind : uint8_t {
Import = 1 << 0,
Framework = 1 << 1
Framework = 1 << 1,
CompilerPlugin = 1 << 2
};
} // end anonymous namespace

Expand Down Expand Up @@ -680,10 +682,14 @@ ASTContext::ASTContext(
getImpl().SearchPathsSet[path] |= SearchPathKind::Import;
for (const auto &framepath : SearchPathOpts.getFrameworkSearchPaths())
getImpl().SearchPathsSet[framepath.Path] |= SearchPathKind::Framework;
for (StringRef path : SearchPathOpts.getCompilerPluginLibraryPaths())
getImpl().SearchPathsSet[path] |= SearchPathKind::CompilerPlugin;

// Register any request-evaluator functions available at the AST layer.
registerAccessRequestFunctions(evaluator);
registerNameLookupRequestFunctions(evaluator);

loadCompilerPlugins();
}

ASTContext::~ASTContext() {
Expand Down Expand Up @@ -1118,6 +1124,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
case KnownProtocolKind::UnsafeCxxRandomAccessIterator:
M = getLoadedModule(Id_Cxx);
break;
case KnownProtocolKind::CompilerPlugin:
M = getLoadedModule(Id_CompilerPluginSupport);
break;
default:
M = getStdlibModule();
break;
Expand Down Expand Up @@ -6032,3 +6041,24 @@ BuiltinTupleType *ASTContext::getBuiltinTupleType() {

return result;
}

CompilerPlugin *ASTContext::getLoadedPlugin(StringRef name) {
auto lookup = LoadedPlugins.find(name);
if (lookup == LoadedPlugins.end())
return nullptr;
return &lookup->second;
}

void *ASTContext::getAddressOfSymbol(StringRef name,
llvm::sys::DynamicLibrary *libraryHint) {
auto lookup = LoadedSymbols.try_emplace(name, nullptr);
void *&address = lookup.first->getValue();
if (lookup.second) {
if (libraryHint && libraryHint->isValid())
address = libraryHint->getAddressOfSymbol(name.data());
else
address = llvm::sys::DynamicLibrary::
SearchForAddressOfSymbol(name.data());
}
return address;
}
8 changes: 8 additions & 0 deletions lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ add_swift_host_library(swiftAST STATIC
CaptureInfo.cpp
ClangSwiftTypeCorrespondence.cpp
ClangTypeConverter.cpp
CompilerPlugin.cpp
ConcreteDeclRef.cpp
ConformanceLookupTable.cpp
Decl.cpp
Expand Down Expand Up @@ -156,6 +157,13 @@ if(NOT SWIFT_BUILD_ONLY_SYNTAXPARSERLIB)
clangBasic)
endif()

if(SWIFT_SWIFT_PARSER)
target_compile_definitions(swiftAST
PRIVATE
SWIFT_SWIFT_PARSER
)
endif()

target_link_libraries(swiftAST
PUBLIC swiftBasic
PRIVATE swiftSyntax)
Expand Down
Loading