Skip to content

Commit 0a55811

Browse files
authored
Merge pull request #65630 from rintaro/macro-plugin-intiialize-rdar108022847
2 parents f984eda + e181a4d commit 0a55811

File tree

9 files changed

+125
-38
lines changed

9 files changed

+125
-38
lines changed

include/swift/AST/CASTBridging.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ void Plugin_setCapability(PluginHandle handle, PluginCapabilityPtr data);
313313
/// Get a capability data set by \c Plugin_setCapability .
314314
PluginCapabilityPtr _Nullable Plugin_getCapability(PluginHandle handle);
315315

316+
/// Get the executable file path of the plugin.
317+
const char *Plugin_getExecutableFilePath(PluginHandle handle);
318+
316319
/// Lock the plugin. Clients should lock it during sending and recving the
317320
/// response.
318321
void Plugin_lock(PluginHandle handle);

include/swift/AST/PluginRegistry.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#ifndef SWIFT_PLUGIN_REGISTRY_H
1313
#define SWIFT_PLUGIN_REGISTRY_H
1414

15+
#include "swift/Basic/StringExtras.h"
1516
#include "llvm/ADT/ArrayRef.h"
1617
#include "llvm/ADT/StringMap.h"
1718
#include "llvm/ADT/StringRef.h"
@@ -139,6 +140,10 @@ class LoadedExecutablePlugin {
139140

140141
llvm::sys::procid_t getPid() { return Process->pid; }
141142

143+
NullTerminatedStringRef getExecutablePath() {
144+
return {ExecutablePath.c_str(), ExecutablePath.size()};
145+
}
146+
142147
const void *getCapability() { return capability; };
143148
void setCapability(const void *newValue) { capability = newValue; };
144149

lib/AST/CASTBridging.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,11 @@ void Plugin_setCapability(PluginHandle handle, PluginCapabilityPtr data) {
642642
plugin->setCapability(data);
643643
}
644644

645+
const char *Plugin_getExecutableFilePath(PluginHandle handle) {
646+
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
647+
return plugin->getExecutablePath().data();
648+
}
649+
645650
void Plugin_lock(PluginHandle handle) {
646651
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
647652
plugin->lock();

lib/AST/PluginRegistry.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ LoadedExecutablePlugin::PluginProcess::~PluginProcess() {
178178

179179
LoadedExecutablePlugin::~LoadedExecutablePlugin() {
180180
// Let ASTGen to cleanup things.
181-
this->cleanup();
181+
if (this->cleanup)
182+
this->cleanup();
182183
}
183184

184185
ssize_t LoadedExecutablePlugin::PluginProcess::read(void *buf,

lib/ASTGen/Sources/ASTGen/PluginHost.swift

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,32 @@ import CBasicBridging
1515
import SwiftSyntax
1616
import swiftLLVMJSON
1717

18-
enum PluginError: Error {
19-
case stalePlugin
20-
case failedToSendMessage
21-
case failedToReceiveMessage
22-
case invalidReponseKind
18+
enum PluginError: String, Error, CustomStringConvertible {
19+
case stalePlugin = "plugin is stale"
20+
case failedToSendMessage = "failed to send request to plugin"
21+
case failedToReceiveMessage = "failed to receive result from plugin"
22+
case invalidReponseKind = "plugin returned invalid result"
23+
24+
var description: String { rawValue }
2325
}
2426

2527
@_cdecl("swift_ASTGen_initializePlugin")
2628
public func _initializePlugin(
27-
opaqueHandle: UnsafeMutableRawPointer
28-
) {
29+
opaqueHandle: UnsafeMutableRawPointer,
30+
cxxDiagnosticEngine: UnsafeMutablePointer<UInt8>?
31+
) -> Bool {
2932
let plugin = CompilerPlugin(opaqueHandle: opaqueHandle)
30-
plugin.initialize()
33+
let diagEngine = PluginDiagnosticsEngine(cxxDiagnosticEngine: cxxDiagnosticEngine)
34+
35+
do {
36+
try plugin.initialize()
37+
return true
38+
} catch {
39+
diagEngine?.diagnose(
40+
message: "compiler plugin not loaded: '\(plugin.executableFilePath); failed to initialize",
41+
severity: .warning)
42+
return false
43+
}
3144
}
3245

3346
@_cdecl("swift_ASTGen_deinitializePlugin")
@@ -48,17 +61,19 @@ func swift_ASTGen_pluginServerLoadLibraryPlugin(
4861
cxxDiagnosticEngine: UnsafeMutablePointer<UInt8>?
4962
) -> Bool {
5063
let plugin = CompilerPlugin(opaqueHandle: opaqueHandle)
64+
let diagEngine = PluginDiagnosticsEngine(cxxDiagnosticEngine: cxxDiagnosticEngine)
65+
66+
if plugin.capability?.features.contains(.loadPluginLibrary) != true {
67+
// This happens only if invalid plugin server was passed to `-external-plugin-path`.
68+
diagEngine?.diagnose(
69+
message: "compiler plugin not loaded: '\(libraryPath); invalid plugin server",
70+
severity: .warning)
71+
return false
72+
}
5173
assert(plugin.capability?.features.contains(.loadPluginLibrary) == true)
5274
let libraryPath = String(cString: libraryPath)
5375
let moduleName = String(cString: moduleName)
5476

55-
let diagEngine: PluginDiagnosticsEngine?
56-
if let cxxDiagnosticEngine = cxxDiagnosticEngine {
57-
diagEngine = PluginDiagnosticsEngine(cxxDiagnosticEngine: cxxDiagnosticEngine)
58-
} else {
59-
diagEngine = nil
60-
}
61-
6277
do {
6378
let result = try plugin.sendMessageAndWaitWithoutLock(
6479
.loadPluginLibrary(libraryPath: libraryPath, moduleName: moduleName)
@@ -69,7 +84,9 @@ func swift_ASTGen_pluginServerLoadLibraryPlugin(
6984
diagEngine?.emit(diagnostics);
7085
return loaded
7186
} catch {
72-
diagEngine?.diagnose(error: error)
87+
diagEngine?.diagnose(
88+
message: "compiler plugin not loaded: '\(libraryPath); \(error)",
89+
severity: .warning)
7390
return false
7491
}
7592
}
@@ -136,20 +153,15 @@ struct CompilerPlugin {
136153
}
137154

138155
/// Initialize the plugin. This should be called inside lock.
139-
func initialize() {
140-
do {
141-
// Get capability.
142-
let response = try self.sendMessageAndWaitWithoutLock(.getCapability)
143-
guard case .getCapabilityResult(let capability) = response else {
144-
throw PluginError.invalidReponseKind
145-
}
146-
let ptr = UnsafeMutablePointer<Capability>.allocate(capacity: 1)
147-
ptr.initialize(to: .init(capability))
148-
Plugin_setCapability(opaqueHandle, UnsafeRawPointer(ptr))
149-
} catch {
150-
assertionFailure(String(describing: error))
151-
return
156+
func initialize() throws {
157+
// Get capability.
158+
let response = try self.sendMessageAndWaitWithoutLock(.getCapability)
159+
guard case .getCapabilityResult(let capability) = response else {
160+
throw PluginError.invalidReponseKind
152161
}
162+
let ptr = UnsafeMutablePointer<Capability>.allocate(capacity: 1)
163+
ptr.initialize(to: .init(capability))
164+
Plugin_setCapability(opaqueHandle, UnsafeRawPointer(ptr))
153165
}
154166

155167
func deinitialize() {
@@ -169,6 +181,10 @@ struct CompilerPlugin {
169181
}
170182
return nil
171183
}
184+
185+
var executableFilePath: String {
186+
return String(cString: Plugin_getExecutableFilePath(opaqueHandle))
187+
}
172188
}
173189

174190
class PluginDiagnosticsEngine {
@@ -179,6 +195,14 @@ class PluginDiagnosticsEngine {
179195
self.cxxDiagnosticEngine = cxxDiagnosticEngine
180196
}
181197

198+
/// Failable convenience initializer for optional cxx engine pointer.
199+
convenience init?(cxxDiagnosticEngine: UnsafeMutablePointer<UInt8>?) {
200+
guard let cxxDiagnosticEngine = cxxDiagnosticEngine else {
201+
return nil
202+
}
203+
self.init(cxxDiagnosticEngine: cxxDiagnosticEngine)
204+
}
205+
182206
/// Register an 'ExportedSourceFile' to the engine. So the engine can get
183207
/// C++ SourceLoc from a pair of filename and offset.
184208
func add(exportedSourceFile: UnsafePointer<ExportedSourceFile>) {
@@ -282,6 +306,10 @@ class PluginDiagnosticsEngine {
282306
)
283307
}
284308

309+
func diagnose(message: String, severity: PluginMessage.Diagnostic.Severity) {
310+
self.emitSingle(message: message, severity: severity, position: .invalid)
311+
}
312+
285313
/// Produce the C++ source location for a given position based on a
286314
/// syntax node.
287315
private func cxxSourceLocation(

lib/Sema/TypeCheckMacros.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/AST/ASTMangler.h"
2222
#include "swift/AST/ASTNode.h"
2323
#include "swift/AST/CASTBridging.h"
24+
#include "swift/AST/DiagnosticsFrontend.h"
2425
#include "swift/AST/Expr.h"
2526
#include "../AST/InlinableText.h"
2627
#include "swift/AST/MacroDefinition.h"
@@ -75,7 +76,7 @@ extern "C" ptrdiff_t swift_ASTGen_expandAttachedMacro(
7576
void *parentDeclSourceFile, const void *parentDeclSourceLocation,
7677
const char **evaluatedSource, ptrdiff_t *evaluatedSourceLength);
7778

78-
extern "C" void swift_ASTGen_initializePlugin(void *handle);
79+
extern "C" bool swift_ASTGen_initializePlugin(void *handle, void *diagEngine);
7980
extern "C" void swift_ASTGen_deinitializePlugin(void *handle);
8081
extern "C" bool swift_ASTGen_pluginServerLoadLibraryPlugin(
8182
void *handle, const char *libraryPath, const char *moduleName,
@@ -316,7 +317,9 @@ loadExecutablePluginByName(ASTContext &ctx, Identifier moduleName) {
316317
// But plugin loading is in libAST and it can't link ASTGen symbols.
317318
if (!executablePlugin->isInitialized()) {
318319
#if SWIFT_SWIFT_PARSER
319-
swift_ASTGen_initializePlugin(executablePlugin);
320+
if (!swift_ASTGen_initializePlugin(executablePlugin, &ctx.Diags)) {
321+
return nullptr;
322+
}
320323
executablePlugin->setCleanup([executablePlugin] {
321324
swift_ASTGen_deinitializePlugin(executablePlugin);
322325
});
@@ -328,7 +331,9 @@ loadExecutablePluginByName(ASTContext &ctx, Identifier moduleName) {
328331
#if SWIFT_SWIFT_PARSER
329332
llvm::SmallString<128> resolvedLibraryPath;
330333
auto fs = ctx.SourceMgr.getFileSystem();
331-
if (fs->getRealPath(libraryPath, resolvedLibraryPath)) {
334+
if (auto err = fs->getRealPath(libraryPath, resolvedLibraryPath)) {
335+
ctx.Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded,
336+
executablePluginPath, err.message());
332337
return nullptr;
333338
}
334339
std::string resolvedLibraryPathStr(resolvedLibraryPath);

test/Macros/macro_plugin_broken.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// REQUIRES: swift_swift_parser
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file %s %t
5+
6+
// RUN: %host-build-swift \
7+
// RUN: -swift-version 5 -o %t/broken-plugin \
8+
// RUN: -module-name=TestPlugin \
9+
// RUN: %t/broken_plugin.swift \
10+
// RUN: -g -no-toolchain-stdlib-rpath -swift-version 5
11+
12+
// RUN: not %swift-target-frontend \
13+
// RUN: -typecheck \
14+
// RUN: -swift-version 5 -enable-experimental-feature Macros \
15+
// RUN: -load-plugin-executable %t/broken-plugin#TestPlugin \
16+
// RUN: -module-name MyApp \
17+
// RUN: -serialize-diagnostics-path %t/macro_expand.dia \
18+
// RUN: %t/test.swift
19+
20+
// RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix CHECK %s
21+
22+
// CHECK: (null):0:0: warning: compiler plugin not loaded: {{.+}}broken-plugin; failed to initialize
23+
// CHECK: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro';
24+
// CHECK: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro';
25+
// CHECK: +-{{.+}}test.swift:1:33: note: 'fooMacro' declared here
26+
27+
//--- test.swift
28+
@freestanding(expression) macro fooMacro(_: Any) -> String = #externalMacro(module: "TestPlugin", type: "FooMacro")
29+
30+
func test() {
31+
_ = #fooMacro(1)
32+
}
33+
34+
//--- broken_plugin.swift
35+
func minusTen(value: UInt) -> UInt {
36+
// Causes crash.
37+
return value - 10
38+
}
39+
40+
_ = minusTen(value: 5)

test/Macros/macro_plugin_error.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323

2424
// CHECK: ->(plugin:[[#PID1:]]) {"getCapability":{}}
2525
// CHECK-NEXT: <-(plugin:[[#PID1]]) {"getCapabilityResult":{"capability":{"protocolVersion":1}}}
26-
// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":6,"offset":200},"source":"#fooMacro(1)"}}}
26+
// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":6,"offset":[[#]]},"source":"#fooMacro(1)"}}}
2727
// CHECK-NEXT: <-(plugin:[[#PID1]]) {"invalidResponse":{}}
28-
// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":8,"offset":304},"source":"#fooMacro(2)"}}}
28+
// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":8,"offset":[[#]]},"source":"#fooMacro(2)"}}}
2929
// ^ This messages causes the mock plugin exit because there's no matching expected message.
3030

31-
// CHECK: ->(plugin:[[#PID2:]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":10,"offset":386},"source":"#fooMacro(3)"}}}
31+
// CHECK: ->(plugin:[[#PID2:]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":10,"offset":[[#]]},"source":"#fooMacro(3)"}}}
3232
// CHECK-NEXT: <-(plugin:[[#PID2:]]) {"expandFreestandingMacroResult":{"diagnostics":[],"expandedSource":"3.description"}}
3333

3434
//--- test.swift
@@ -40,7 +40,7 @@ func test() {
4040
let _: String = #fooMacro(1)
4141
// expected-error @-1 {{typeMismatch(swiftASTGen.PluginToHostMessage}}
4242
let _: String = #fooMacro(2)
43-
// expected-error @-1 {{failedToReceiveMessage}}
43+
// expected-error @-1 {{failed to receive result from plugin (from macro 'fooMacro')}}
4444
let _: String = #fooMacro(3)
4545
// ^ This should succeed.
4646
}

test/Macros/macro_plugin_server.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func testStringify(a: Int, b: Int) {
5656
let s1: String = #stringify(a + b).1
5757
print(s1)
5858

59-
// expected-error @+1 {{failedToReceiveMessage (from macro 'evil')}}
59+
// expected-error @+1 {{failed to receive result from plugin (from macro 'evil')}}
6060
let s2: String = #evil(42)
6161
print(s2)
6262

0 commit comments

Comments
 (0)