diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index ebded4d931589..decba614eef65 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -536,9 +536,6 @@ 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)) - ERROR(dont_enable_interop_and_compat,none, "do not pass both '-enable-experimental-cxx-interop' and " "'-cxx-interoperability-mode'; remove '-enable-experimental-cxx-interop'", ()) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0485345d4c5da..2699c5ed6863c 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -7312,7 +7312,7 @@ ERROR(macro_undefined,PointsToFirstBadToken, "no macro named %0", (Identifier)) ERROR(external_macro_not_found,none, "external macro implementation type '%0.%1' could not be found for " - "macro %2", (StringRef, StringRef, DeclName)) + "macro %2; %3", (StringRef, StringRef, DeclName, StringRef)) ERROR(macro_must_be_defined,none, "macro %0 requires a definition", (DeclName)) ERROR(external_macro_outside_macro_definition,none, diff --git a/include/swift/AST/MacroDefinition.h b/include/swift/AST/MacroDefinition.h index 3fd4377cb5d60..ec7b056734277 100644 --- a/include/swift/AST/MacroDefinition.h +++ b/include/swift/AST/MacroDefinition.h @@ -18,6 +18,7 @@ #ifndef SWIFT_AST_MACRO_DEFINITION_H #define SWIFT_AST_MACRO_DEFINITION_H +#include "swift/Basic/StringExtras.h" #include "llvm/ADT/PointerUnion.h" namespace swift { @@ -26,14 +27,25 @@ class ASTContext; /// A reference to an external macro definition that is understood by ASTGen. struct ExternalMacroDefinition { - enum class PluginKind { + enum class PluginKind : int8_t { InProcess = 0, Executable = 1, + Error = -1, }; PluginKind kind; /// ASTGen's notion of an macro definition, which is opaque to the C++ part - /// of the compiler. - void *opaqueHandle = nullptr; + /// of the compiler. If 'kind' is 'PluginKind::Error', this is a C-string to + /// the error message + const void *opaqueHandle = nullptr; + + static ExternalMacroDefinition error(NullTerminatedStringRef message) { + return ExternalMacroDefinition{PluginKind::Error, + static_cast(message.data())}; + } + bool isError() const { return kind == PluginKind::Error; } + NullTerminatedStringRef getErrorMessage() const { + return static_cast(opaqueHandle); + } }; /// A reference to an external macro. diff --git a/include/swift/AST/PluginLoader.h b/include/swift/AST/PluginLoader.h index 229161515f867..43b2ff8413a9f 100644 --- a/include/swift/AST/PluginLoader.h +++ b/include/swift/AST/PluginLoader.h @@ -76,14 +76,15 @@ class PluginLoader { /// returns a nullptr. /// NOTE: This method is idempotent. If the plugin is already loaded, the same /// instance is simply returned. - LoadedLibraryPlugin *loadLibraryPlugin(llvm::StringRef path); + llvm::Expected loadLibraryPlugin(llvm::StringRef path); /// Launch the specified executable plugin path resolving the path with the /// current VFS. If it fails to load the plugin, a diagnostic is emitted, and /// returns a nullptr. /// NOTE: This method is idempotent. If the plugin is already loaded, the same /// instance is simply returned. - LoadedExecutablePlugin *loadExecutablePlugin(llvm::StringRef path); + llvm::Expected + loadExecutablePlugin(llvm::StringRef path); }; } // namespace swift diff --git a/include/swift/AST/PluginRegistry.h b/include/swift/AST/PluginRegistry.h index ff8406f51b352..3ffcc4abb982e 100644 --- a/include/swift/AST/PluginRegistry.h +++ b/include/swift/AST/PluginRegistry.h @@ -33,11 +33,19 @@ class LoadedLibraryPlugin { /// Cache of loaded symbols. llvm::StringMap resolvedSymbols; + /// Path to the plugin library. + const std::string LibraryPath; + public: - LoadedLibraryPlugin(void *handle) : handle(handle) {} + LoadedLibraryPlugin(void *handle, StringRef path) + : handle(handle), LibraryPath(path) {} /// Finds the address of the given symbol within the library. void *getAddressOfSymbol(const char *symbolName); + + NullTerminatedStringRef getLibraryPath() { + return {LibraryPath.c_str(), LibraryPath.size()}; + } }; /// Represent a "resolved" executable plugin. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 77ec1aaccee61..16a5e713362ba 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -4238,25 +4238,48 @@ class ExpandSynthesizedMemberMacroRequest }; /// Represent a loaded plugin either an in-process library or an executable. -class LoadedCompilerPlugin { - llvm::PointerUnion ptr; +class CompilerPluginLoadResult { + enum class PluginKind : uint8_t { + Error = 0, + Library, + Executable, + }; + PluginKind Kind; + void *opaqueHandle; + + CompilerPluginLoadResult(PluginKind K, void *opaque) + : Kind(K), opaqueHandle(opaque) {} public: - LoadedCompilerPlugin(std::nullptr_t) : ptr(nullptr) {} - LoadedCompilerPlugin(LoadedLibraryPlugin *ptr) : ptr(ptr){}; - LoadedCompilerPlugin(LoadedExecutablePlugin *ptr) : ptr(ptr){}; + CompilerPluginLoadResult(LoadedLibraryPlugin *ptr) + : CompilerPluginLoadResult(PluginKind::Library, ptr){}; + CompilerPluginLoadResult(LoadedExecutablePlugin *ptr) + : CompilerPluginLoadResult(PluginKind::Executable, ptr){}; + static CompilerPluginLoadResult error(NullTerminatedStringRef message) { + return CompilerPluginLoadResult(PluginKind::Error, + const_cast(message.data())); + } LoadedLibraryPlugin *getAsLibraryPlugin() const { - return ptr.dyn_cast(); + if (Kind != PluginKind::Library) + return nullptr; + return static_cast(opaqueHandle); } LoadedExecutablePlugin *getAsExecutablePlugin() const { - return ptr.dyn_cast(); + if (Kind != PluginKind::Executable) + return nullptr; + return static_cast(opaqueHandle); + } + bool isError() const { return Kind == PluginKind::Error; } + NullTerminatedStringRef getErrorMessage() const { + assert(isError()); + return static_cast(opaqueHandle); } }; class CompilerPluginLoadRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -4264,8 +4287,8 @@ class CompilerPluginLoadRequest private: friend SimpleRequest; - LoadedCompilerPlugin evaluate(Evaluator &evaluator, ASTContext *ctx, - Identifier moduleName) const; + CompilerPluginLoadResult evaluate(Evaluator &evaluator, ASTContext *ctx, + Identifier moduleName) const; public: // Source location @@ -4296,8 +4319,8 @@ class ExpandPeerMacroRequest /// Resolve an external macro given its module and type name. class ExternalMacroDefinitionRequest : public SimpleRequest( - ASTContext *, Identifier, Identifier), + ExternalMacroDefinition(ASTContext *, Identifier, + Identifier), RequestFlags::Cached> { public: using SimpleRequest::SimpleRequest; @@ -4305,10 +4328,9 @@ class ExternalMacroDefinitionRequest private: friend SimpleRequest; - llvm::Optional evaluate( - Evaluator &evaluator, ASTContext *ctx, - Identifier moduleName, Identifier typeName - ) const; + ExternalMacroDefinition evaluate(Evaluator &evaluator, ASTContext *ctx, + Identifier moduleName, + Identifier typeName) const; public: // Source location diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index d308429c1c3ab..6644b90bd3a52 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -458,10 +458,10 @@ SWIFT_REQUEST(TypeChecker, MacroDefinitionRequest, MacroDefinition(MacroDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, CompilerPluginLoadRequest, - LoadedCompilerPlugin(ASTContext *, Identifier), + CompilerPluginLoadResult(ASTContext *, Identifier), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExternalMacroDefinitionRequest, - Optional(ASTContext *, Identifier, Identifier), + ExternalMacroDefinition(ASTContext *, Identifier, Identifier), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ExpandMacroExpansionDeclRequest, ArrayRef(MacroExpansionDecl *), diff --git a/include/swift/Basic/StringExtras.h b/include/swift/Basic/StringExtras.h index 5a6de72ffb822..affe162b9e07a 100644 --- a/include/swift/Basic/StringExtras.h +++ b/include/swift/Basic/StringExtras.h @@ -21,9 +21,11 @@ #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/Allocator.h" #include #include @@ -492,13 +494,21 @@ class NullTerminatedStringRef { /// Create a null-terminated string, copying \p Str into \p A . template - NullTerminatedStringRef(StringRef Str, Allocator &A) : Ref("") { - if (Str.empty()) + NullTerminatedStringRef(llvm::Twine Str, Allocator &A) : Ref("") { + if (Str.isTriviallyEmpty()) return; + if (Str.isSingleStringLiteral()) { + Ref = Str.getSingleStringRef(); + return; + } + llvm::SmallString<0> stash; + auto _ref = Str.toStringRef(stash); - size_t size = Str.size(); - char *memory = A.template Allocate(size + 1); - memcpy(memory, Str.data(), size); + size_t size = _ref.size(); + if (size == 0) + return; + char *memory = static_cast(A.Allocate(size + 1, alignof(char))); + memcpy(memory, _ref.data(), size); memory[size] = '\0'; Ref = {memory, size}; } diff --git a/lib/AST/PluginLoader.cpp b/lib/AST/PluginLoader.cpp index 3a78138fb766f..94b315bdf9376 100644 --- a/lib/AST/PluginLoader.cpp +++ b/lib/AST/PluginLoader.cpp @@ -158,13 +158,12 @@ PluginLoader::lookupPluginByModuleName(Identifier moduleName) { } } -LoadedLibraryPlugin *PluginLoader::loadLibraryPlugin(StringRef path) { +llvm::Expected +PluginLoader::loadLibraryPlugin(StringRef path) { auto fs = Ctx.SourceMgr.getFileSystem(); SmallString<128> resolvedPath; if (auto err = fs->getRealPath(path, resolvedPath)) { - Ctx.Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path, - err.message()); - return nullptr; + return llvm::createStringError(err, err.message()); } // Track the dependency. @@ -174,21 +173,25 @@ LoadedLibraryPlugin *PluginLoader::loadLibraryPlugin(StringRef path) { // Load the plugin. auto plugin = getRegistry()->loadLibraryPlugin(resolvedPath); if (!plugin) { - Ctx.Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path, - llvm::toString(plugin.takeError())); - return nullptr; + resolvedPath.push_back(0); + return llvm::handleErrors( + plugin.takeError(), [&](const llvm::ErrorInfoBase &err) { + return llvm::createStringError( + err.convertToErrorCode(), + "compiler plugin '%s' could not be loaded; %s", + resolvedPath.data(), err.message().data()); + }); } - return plugin.get(); + return plugin; } -LoadedExecutablePlugin *PluginLoader::loadExecutablePlugin(StringRef path) { +llvm::Expected +PluginLoader::loadExecutablePlugin(StringRef path) { auto fs = Ctx.SourceMgr.getFileSystem(); SmallString<128> resolvedPath; if (auto err = fs->getRealPath(path, resolvedPath)) { - Ctx.Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path, - err.message()); - return nullptr; + return llvm::createStringError(err, err.message()); } // Track the dependency. @@ -198,10 +201,15 @@ LoadedExecutablePlugin *PluginLoader::loadExecutablePlugin(StringRef path) { // Load the plugin. auto plugin = getRegistry()->loadExecutablePlugin(resolvedPath); if (!plugin) { - Ctx.Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path, - llvm::toString(plugin.takeError())); - return nullptr; + resolvedPath.push_back(0); + return llvm::handleErrors( + plugin.takeError(), [&](const llvm::ErrorInfoBase &err) { + return llvm::createStringError( + err.convertToErrorCode(), + "compiler plugin '%s' could not be loaded: %s", + resolvedPath.data(), err.message().data()); + }); } - return plugin.get(); + return plugin; } diff --git a/lib/AST/PluginRegistry.cpp b/lib/AST/PluginRegistry.cpp index beceff366b7d2..7e337c8477e8e 100644 --- a/lib/AST/PluginRegistry.cpp +++ b/lib/AST/PluginRegistry.cpp @@ -67,7 +67,7 @@ PluginRegistry::loadLibraryPlugin(StringRef path) { } #endif - storage = std::make_unique(lib); + storage = std::make_unique(lib, path); return storage.get(); } diff --git a/lib/ASTGen/Sources/ASTGen/PluginHost.swift b/lib/ASTGen/Sources/ASTGen/PluginHost.swift index beb7c632ebd58..e0127203237b4 100644 --- a/lib/ASTGen/Sources/ASTGen/PluginHost.swift +++ b/lib/ASTGen/Sources/ASTGen/PluginHost.swift @@ -37,9 +37,8 @@ public func _initializePlugin( try plugin.initialize() return true } catch { - diagEngine?.diagnose( - message: "compiler plugin not loaded: '\(plugin.executableFilePath); failed to initialize", - severity: .warning) + // Don't care the actual error. Probably the plugin is completely broken. + // The failure is diagnosed in the caller. return false } } @@ -59,16 +58,12 @@ func swift_ASTGen_pluginServerLoadLibraryPlugin( opaqueHandle: UnsafeMutableRawPointer, libraryPath: UnsafePointer, moduleName: UnsafePointer, - cxxDiagnosticEngine: UnsafeMutableRawPointer? + errorOut: UnsafeMutablePointer? ) -> Bool { let plugin = CompilerPlugin(opaqueHandle: opaqueHandle) - let diagEngine = PluginDiagnosticsEngine(cxxDiagnosticEngine: cxxDiagnosticEngine) if plugin.capability?.features.contains(.loadPluginLibrary) != true { - // This happens only if invalid plugin server was passed to `-external-plugin-path`. - diagEngine?.diagnose( - message: "compiler plugin not loaded: '\(libraryPath); invalid plugin server", - severity: .warning) + errorOut?.pointee = allocateBridgedString("compiler plugin not loaded: '\(libraryPath); invalid plugin server") return false } assert(plugin.capability?.features.contains(.loadPluginLibrary) == true) @@ -82,12 +77,15 @@ func swift_ASTGen_pluginServerLoadLibraryPlugin( guard case .loadPluginLibraryResult(let loaded, let diagnostics) = result else { throw PluginError.invalidReponseKind } - diagEngine?.emit(diagnostics); - return loaded + if loaded { + assert(diagnostics.isEmpty) + return true + } + var errorMsgs = diagnostics.map({$0.message}).joined(separator: ", "); + errorOut?.pointee = allocateBridgedString(errorMsgs); + return false } catch { - diagEngine?.diagnose( - message: "compiler plugin not loaded: '\(libraryPath); \(error)", - severity: .warning) + errorOut?.pointee = allocateBridgedString("\(error)") return false } } diff --git a/lib/Basic/Sandbox.cpp b/lib/Basic/Sandbox.cpp index 3249b2f38a191..3c60ef9c10101 100644 --- a/lib/Basic/Sandbox.cpp +++ b/lib/Basic/Sandbox.cpp @@ -30,7 +30,7 @@ static StringRef sandboxProfile(llvm::BumpPtrAllocator &Alloc) { // This is required to launch any processes (execve(2)). contents += "(allow process-exec*)\n"; - return NullTerminatedStringRef(contents, Alloc); + return NullTerminatedStringRef(StringRef(contents), Alloc); } #endif diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 2b1e3e718718f..019845c84e0da 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2073,14 +2073,14 @@ class DeclChecker : public DeclVisitor { ExternalMacroDefinitionRequest request{ &Ctx, external.moduleName, external.macroTypeName }; - auto externalDef = evaluateOrDefault(Ctx.evaluator, request, llvm::None); - if (!externalDef) { - MD->diagnose( - diag::external_macro_not_found, - external.moduleName.str(), - external.macroTypeName.str(), - MD->getName() - ).limitBehavior(DiagnosticBehavior::Warning); + auto externalDef = + evaluateOrDefault(Ctx.evaluator, request, + ExternalMacroDefinition::error("unknown error")); + if (externalDef.isError()) { + MD->diagnose(diag::external_macro_not_found, external.moduleName.str(), + external.macroTypeName.str(), MD->getName(), + externalDef.getErrorMessage()) + .limitBehavior(DiagnosticBehavior::Warning); } break; diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 8c5908dac414d..75cf81518066f 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -65,12 +65,12 @@ extern "C" void swift_ASTGen_freeExpansionReplacements( ptrdiff_t numReplacements); extern "C" ptrdiff_t swift_ASTGen_expandFreestandingMacro( - void *diagEngine, void *macro, uint8_t externalKind, + void *diagEngine, const void *macro, uint8_t externalKind, const char *discriminator, uint8_t rawMacroRole, void *sourceFile, const void *sourceLocation, BridgedString *evaluatedSourceOut); extern "C" ptrdiff_t swift_ASTGen_expandAttachedMacro( - void *diagEngine, void *macro, uint8_t externalKind, + void *diagEngine, const void *macro, uint8_t externalKind, const char *discriminator, const char *qualifiedType, const char *conformances, uint8_t rawMacroRole, void *customAttrSourceFile, const void *customAttrSourceLocation, void *declarationSourceFile, @@ -81,7 +81,7 @@ extern "C" bool swift_ASTGen_initializePlugin(void *handle, void *diagEngine); extern "C" void swift_ASTGen_deinitializePlugin(void *handle); extern "C" bool swift_ASTGen_pluginServerLoadLibraryPlugin( void *handle, const char *libraryPath, const char *moduleName, - void *diagEngine); + BridgedString *errorOut); static inline StringRef toStringRef(BridgedString bridged) { return {reinterpret_cast(bridged.data), size_t(bridged.length)}; @@ -277,7 +277,7 @@ MacroDefinition MacroDefinitionRequest::evaluate( #endif } -static LoadedExecutablePlugin * +static llvm::Expected initializeExecutablePlugin(ASTContext &ctx, LoadedExecutablePlugin *executablePlugin, StringRef libraryPath, Identifier moduleName) { @@ -291,7 +291,9 @@ initializeExecutablePlugin(ASTContext &ctx, if (!executablePlugin->isInitialized()) { #if SWIFT_BUILD_SWIFT_SYNTAX if (!swift_ASTGen_initializePlugin(executablePlugin, &ctx.Diags)) { - return nullptr; + return llvm::createStringError( + llvm::inconvertibleErrorCode(), "'%s' produced malformed response", + executablePlugin->getExecutablePath().data()); } // Resend the compiler capability on reconnect. @@ -314,18 +316,25 @@ initializeExecutablePlugin(ASTContext &ctx, llvm::SmallString<128> resolvedLibraryPath; auto fs = ctx.SourceMgr.getFileSystem(); if (auto err = fs->getRealPath(libraryPath, resolvedLibraryPath)) { - ctx.Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, - executablePlugin->getExecutablePath(), err.message()); - return nullptr; + return llvm::createStringError(err, err.message()); } std::string resolvedLibraryPathStr(resolvedLibraryPath); std::string moduleNameStr(moduleName.str()); + BridgedString errorOut{nullptr, 0}; bool loaded = swift_ASTGen_pluginServerLoadLibraryPlugin( executablePlugin, resolvedLibraryPathStr.c_str(), moduleNameStr.c_str(), - &ctx.Diags); - if (!loaded) - return nullptr; + &errorOut); + if (!loaded) { + SWIFT_DEFER { swift_ASTGen_freeBridgedString(errorOut); }; + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "failed to load library plugin '%s' in plugin server '%s'; %s", + resolvedLibraryPathStr.c_str(), + executablePlugin->getExecutablePath().data(), errorOut.data); + } + + assert(errorOut.data == nullptr); // Set a callback to load the library again on reconnections. auto *callback = new std::function( @@ -333,7 +342,7 @@ initializeExecutablePlugin(ASTContext &ctx, (void)swift_ASTGen_pluginServerLoadLibraryPlugin( executablePlugin, resolvedLibraryPathStr.c_str(), moduleNameStr.c_str(), - /*diags=*/nullptr); + /*errorOut=*/nullptr); }); executablePlugin->addOnReconnect(callback); @@ -348,40 +357,66 @@ initializeExecutablePlugin(ASTContext &ctx, return executablePlugin; } -LoadedCompilerPlugin +CompilerPluginLoadResult CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx, Identifier moduleName) const { PluginLoader &loader = ctx->getPluginLoader(); const auto &entry = loader.lookupPluginByModuleName(moduleName); + SmallString<0> errorMessage; + if (!entry.executablePath.empty()) { - if (LoadedExecutablePlugin *executablePlugin = - loader.loadExecutablePlugin(entry.executablePath)) { + llvm::Expected executablePlugin = + loader.loadExecutablePlugin(entry.executablePath); + if (executablePlugin) { if (ctx->LangOpts.EnableMacroLoadingRemarks) { unsigned tag = entry.libraryPath.empty() ? 1 : 2; ctx->Diags.diagnose(SourceLoc(), diag::macro_loaded, moduleName, tag, entry.executablePath, entry.libraryPath); } - return initializeExecutablePlugin(*ctx, executablePlugin, - entry.libraryPath, moduleName); + executablePlugin = initializeExecutablePlugin( + *ctx, executablePlugin.get(), entry.libraryPath, moduleName); } + if (executablePlugin) + return executablePlugin.get(); + llvm::handleAllErrors(executablePlugin.takeError(), + [&](const llvm::ErrorInfoBase &err) { + if (!errorMessage.empty()) + errorMessage += ", "; + errorMessage += err.message(); + }); } else if (!entry.libraryPath.empty()) { - if (LoadedLibraryPlugin *libraryPlugin = - loader.loadLibraryPlugin(entry.libraryPath)) { + + llvm::Expected libraryPlugin = + loader.loadLibraryPlugin(entry.libraryPath); + if (libraryPlugin) { if (ctx->LangOpts.EnableMacroLoadingRemarks) { ctx->Diags.diagnose(SourceLoc(), diag::macro_loaded, moduleName, 0, entry.libraryPath, StringRef()); } - return libraryPlugin; + return libraryPlugin.get(); + } else { + llvm::handleAllErrors(libraryPlugin.takeError(), + [&](const llvm::ErrorInfoBase &err) { + if (!errorMessage.empty()) + errorMessage += ", "; + errorMessage += err.message(); + }); } } - - return nullptr; + if (!errorMessage.empty()) { + NullTerminatedStringRef err(errorMessage, *ctx); + return CompilerPluginLoadResult::error(err); + } else { + NullTerminatedStringRef errMsg( + "plugin for module '" + moduleName.str() + "' not found", *ctx); + return CompilerPluginLoadResult::error(errMsg); + } } -static llvm::Optional +static ExternalMacroDefinition resolveInProcessMacro(ASTContext &ctx, Identifier moduleName, Identifier typeName, LoadedLibraryPlugin *plugin) { #if SWIFT_BUILD_SWIFT_SYNTAX @@ -398,13 +433,27 @@ resolveInProcessMacro(ASTContext &ctx, Identifier moduleName, return ExternalMacroDefinition{ ExternalMacroDefinition::PluginKind::InProcess, inProcess}; + } else { + NullTerminatedStringRef err( + "'" + moduleName.str() + "." + typeName.str() + + "' is not a valid macro implementation type in library plugin '" + + StringRef(plugin->getLibraryPath()) + "'", + ctx); + + return ExternalMacroDefinition::error(err); } } + NullTerminatedStringRef err("'" + moduleName.str() + "." + typeName.str() + + "' could not be found in library plugin '" + + StringRef(plugin->getLibraryPath()) + "'", + ctx); + return ExternalMacroDefinition::error(err); #endif - return llvm::None; + return ExternalMacroDefinition::error( + "the current compiler was not built with macro support"); } -static llvm::Optional +static ExternalMacroDefinition resolveExecutableMacro(ASTContext &ctx, LoadedExecutablePlugin *executablePlugin, Identifier moduleName, Identifier typeName) { @@ -417,34 +466,38 @@ resolveExecutableMacro(ASTContext &ctx, return ExternalMacroDefinition{ ExternalMacroDefinition::PluginKind::Executable, execMacro}; } + // NOTE: this is not reachable because executable macro resolution always + // succeeds. + NullTerminatedStringRef err( + "'" + moduleName.str() + "." + typeName.str() + + "' could not be found in executable plugin" + + StringRef(executablePlugin->getExecutablePath()), + ctx); + return ExternalMacroDefinition::error(err); #endif - return llvm::None; + return ExternalMacroDefinition::error( + "the current compiler was not built with macro support"); } -llvm::Optional +ExternalMacroDefinition ExternalMacroDefinitionRequest::evaluate(Evaluator &evaluator, ASTContext *ctx, Identifier moduleName, Identifier typeName) const { // Try to load a plugin module from the plugin search paths. If it // succeeds, resolve in-process from that plugin CompilerPluginLoadRequest loadRequest{ctx, moduleName}; - LoadedCompilerPlugin loaded = - evaluateOrDefault(evaluator, loadRequest, nullptr); + CompilerPluginLoadResult loaded = evaluateOrDefault( + evaluator, loadRequest, CompilerPluginLoadResult::error("request error")); if (auto loadedLibrary = loaded.getAsLibraryPlugin()) { - if (auto inProcess = resolveInProcessMacro( - *ctx, moduleName, typeName, loadedLibrary)) - return *inProcess; + return resolveInProcessMacro(*ctx, moduleName, typeName, loadedLibrary); } if (auto *executablePlugin = loaded.getAsExecutablePlugin()) { - if (auto executableMacro = resolveExecutableMacro(*ctx, executablePlugin, - moduleName, typeName)) { - return executableMacro; - } + return resolveExecutableMacro(*ctx, executablePlugin, moduleName, typeName); } - return llvm::None; + return ExternalMacroDefinition::error(loaded.getErrorMessage()); } /// Adjust the given mangled name for a macro expansion to produce a valid @@ -1028,11 +1081,14 @@ evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, auto external = macroDef.getExternalMacro(); ExternalMacroDefinitionRequest request{&ctx, external.moduleName, external.macroTypeName}; - auto externalDef = evaluateOrDefault(ctx.evaluator, request, llvm::None); - if (!externalDef) { + auto externalDef = + evaluateOrDefault(ctx.evaluator, request, + ExternalMacroDefinition::error("request error")); + if (externalDef.isError()) { ctx.Diags.diagnose(loc, diag::external_macro_not_found, external.moduleName.str(), - external.macroTypeName.str(), macro->getName()); + external.macroTypeName.str(), macro->getName(), + externalDef.getErrorMessage()); macro->diagnose(diag::decl_declared_here, macro); return nullptr; } @@ -1062,9 +1118,10 @@ evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, return nullptr; BridgedString evaluatedSourceOut{nullptr, 0}; + assert(!externalDef.isError()); swift_ASTGen_expandFreestandingMacro( - &ctx.Diags, externalDef->opaqueHandle, - static_cast(externalDef->kind), discriminator->c_str(), + &ctx.Diags, externalDef.opaqueHandle, + static_cast(externalDef.kind), discriminator->c_str(), getRawMacroRole(macroRole), astGenSourceFile, expansion->getSourceRange().Start.getOpaquePointerValue(), &evaluatedSourceOut); @@ -1301,13 +1358,14 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, ExternalMacroDefinitionRequest request{ &ctx, external.moduleName, external.macroTypeName }; - auto externalDef = evaluateOrDefault(ctx.evaluator, request, llvm::None); - if (!externalDef) { + auto externalDef = + evaluateOrDefault(ctx.evaluator, request, + ExternalMacroDefinition::error("failed request")); + if (externalDef.isError()) { attachedTo->diagnose(diag::external_macro_not_found, - external.moduleName.str(), - external.macroTypeName.str(), - macro->getName() - ); + external.moduleName.str(), + external.macroTypeName.str(), macro->getName(), + externalDef.getErrorMessage()); macro->diagnose(diag::decl_declared_here, macro); return nullptr; } @@ -1339,9 +1397,10 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, searchDecl = var->getParentPatternBinding(); BridgedString evaluatedSourceOut{nullptr, 0}; + assert(!externalDef.isError()); swift_ASTGen_expandAttachedMacro( - &ctx.Diags, externalDef->opaqueHandle, - static_cast(externalDef->kind), discriminator->c_str(), + &ctx.Diags, externalDef.opaqueHandle, + static_cast(externalDef.kind), discriminator->c_str(), extendedType.c_str(), conformanceList.c_str(), getRawMacroRole(role), astGenAttrSourceFile, attr->AtLoc.getOpaquePointerValue(), astGenDeclSourceFile, searchDecl->getStartLoc().getOpaquePointerValue(), diff --git a/test/Macros/Inputs/syntax_macro_definitions.swift b/test/Macros/Inputs/syntax_macro_definitions.swift index b1a80605d5877..a6a239b97d307 100644 --- a/test/Macros/Inputs/syntax_macro_definitions.swift +++ b/test/Macros/Inputs/syntax_macro_definitions.swift @@ -2071,3 +2071,12 @@ public struct FakeCodeItemMacro: DeclarationMacro, PeerMacro { return ["if true { return }"] } } + +public struct NotMacroStruct { + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + fatalError() + } +} diff --git a/test/Macros/macro_expand.swift b/test/Macros/macro_expand.swift index 7c0651df81bc5..57607615961bb 100644 --- a/test/Macros/macro_expand.swift +++ b/test/Macros/macro_expand.swift @@ -612,3 +612,12 @@ macro DefineComparableType() = #externalMacro(module: "MacroDefinition", type: " struct HasNestedType { #DefineComparableType } + +#if TEST_DIAGNOSTICS +@freestanding(expression) +macro missingMacro() = #externalMacro(module: "MacroDefinition", type: "BluhBlah") +// expected-warning@-1 {{external macro implementation type 'MacroDefinition.BluhBlah' could not be found for macro 'missingMacro()'; 'MacroDefinition.BluhBlah' could not be found in library plugin '}} +@freestanding(expression) +macro notMacro() = #externalMacro(module: "MacroDefinition", type: "NotMacroStruct") +// expected-warning@-1 {{macro implementation type 'MacroDefinition.NotMacroStruct' could not be found for macro 'notMacro()'; 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '}} +#endif diff --git a/test/Macros/macro_plugin_broken.swift b/test/Macros/macro_plugin_broken.swift index cd20d18a96c83..3cf4b613c1a5a 100644 --- a/test/Macros/macro_plugin_broken.swift +++ b/test/Macros/macro_plugin_broken.swift @@ -18,9 +18,8 @@ // RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix CHECK %s -// CHECK: (null):0:0: warning: compiler plugin not loaded: {{.+}}broken-plugin; failed to initialize -// CHECK: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro' -// CHECK: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro' +// CHECK: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; '{{.*}}broken-plugin' produced malformed response +// CHECK: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; '{{.*}}broken-plugin' produced malformed response // CHECK: +-{{.+}}test.swift:1:33: note: 'fooMacro' declared here //--- test.swift diff --git a/test/Macros/macro_plugin_broken_shlib.swift b/test/Macros/macro_plugin_broken_shlib.swift new file mode 100644 index 0000000000000..d3f90d0c05725 --- /dev/null +++ b/test/Macros/macro_plugin_broken_shlib.swift @@ -0,0 +1,47 @@ +// REQUIRES: swift_swift_parser +// REQUIRES: OS=macosx + +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/plugins) +// RUN: split-file %s %t + +// RUN: touch %t/plugins/libTestPlugin.dylib + +// RUN: not %swift-target-frontend \ +// RUN: -typecheck \ +// RUN: -swift-version 5 \ +// RUN: -external-plugin-path %t/plugins#%swift-plugin-server \ +// RUN: -module-name MyApp \ +// RUN: -serialize-diagnostics-path %t/macro_expand.dia \ +// RUN: %t/test.swift + +// RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix SERVER %s + +// SERVER-NOT: {{error|warning}} +// SERVER: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; failed to load library plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' in plugin server 'BUILD_DIR/{{.*}}/swift-plugin-server'; loader error: dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, {{.*}}): tried: 'BUILD_DIR/{{.*}}/plugins/libTestPlugin.dylib' +// SERVER: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; failed to load library plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' in plugin server 'BUILD_DIR/{{.*}}/swift-plugin-server'; loader error: dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, {{.*}}): tried: 'BUILD_DIR/{{.*}}/plugins/libTestPlugin.dylib' +// SERVER: test.swift:1:33: note: 'fooMacro' declared here +// SERVER-NOT: {{error|warning}} + +// RUN: not %swift-target-frontend \ +// RUN: -typecheck \ +// RUN: -swift-version 5 \ +// RUN: -plugin-path %t/plugins \ +// RUN: -module-name MyApp \ +// RUN: -serialize-diagnostics-path %t/macro_expand_inproc.dia \ +// RUN: %t/test.swift + +// RUN: c-index-test -read-diagnostics %t/macro_expand_inproc.dia 2>&1 | %FileCheck -check-prefix INPROC %s + +// INPROC-NOT: {{error|warning}} +// INPROC: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; compiler plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' could not be loaded; dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' +// INPROC: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; compiler plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' could not be loaded; dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' +// INPROC: test.swift:1:33: note: 'fooMacro' declared here +// INPROC-NOT: {{error|warning}} + +//--- test.swift +@freestanding(expression) macro fooMacro(_: Any) -> String = #externalMacro(module: "TestPlugin", type: "FooMacro") + +func test() { + _ = #fooMacro(1) +} diff --git a/test/Macros/macro_plugin_server.swift b/test/Macros/macro_plugin_server.swift index 2a99bb19f0d10..cd8822e73f1f9 100644 --- a/test/Macros/macro_plugin_server.swift +++ b/test/Macros/macro_plugin_server.swift @@ -64,8 +64,17 @@ // CHECK-NEXT: ->(plugin:[[#PID2]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"MacroDefinition","name":"stringify","typeName":"StringifyMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{{{.+}}},"source":"#stringify(b + a)"}}} // CHECK-NEXT: <-(plugin:[[#PID2]]) {"expandMacroResult":{"diagnostics":[],"expandedSource":"(b + a, \"b + a\")"}} +// CHECK-NEXT ->(plugin:[[#PID2]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"MacroDefinition","name":"missing","typeName":"TypeDoesNotExist"},"macroRole":"expression","syntax":{"kind":"expression","location":{{{*}}},"source":" #missing()"}}} +// CHECK-NEXT <-(plugin:[[#PID2]]) {"expandMacroResult":{"diagnostics":[{"fixIts":[],"highlights":[{{{.*}}}],"message":"macro implementation type 'MacroDefinition.TypeDoesNotExist' could not be found in library plugin '{{.*}}MacroDefinition.{{dylib|so|dll}}'","notes":[],"position":{{{.*}}},"severity":"error"}]}} +// CHECK-NEXT ->(plugin:[[#PID2]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","macro":{"moduleName":"MacroDefinition","name":"missing","typeName":"TypeDoesNotExist"},"macroRole":"expression","syntax":{"kind":"expression","location":{{{*}}},"source":" #notMacro()"}}} +// CHECK-NEXT <-(plugin:[[#PID2]]) {"expandMacroResult":{"diagnostics":[{"fixIts":[],"highlights":[{{{.*}}}],"message":"type 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '{{.*}}MacroDefinition.{{dylib|so|dll}}'","notes":[],"position":{{{.*}}},"severity":"error"}]}} + @freestanding(expression) macro stringify(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro") @freestanding(expression) macro evil(_ value: Int) -> String = #externalMacro(module: "EvilMacros", type: "CrashingMacro") +// FIXME: Diagnose for missing type. +@freestanding(expression) macro missing() = #externalMacro(module: "MacroDefinition", type: "TypeDoesNotExist") +// FIXME: Diagnose for non-macro type. +@freestanding(expression) macro notMacro() = #externalMacro(module: "MacroDefinition", type: "NotMacroStruct") func testStringify(a: Int, b: Int) { let s1: String = #stringify(a + b).1 @@ -77,4 +86,10 @@ func testStringify(a: Int, b: Int) { let s3: String = #stringify(b + a).1 print(s3) + + // expected-error @+1 {{macro implementation type 'MacroDefinition.TypeDoesNotExist' could not be found in library plugin '}} + _ = #missing() + + // expected-error @+1 {{type 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '}} + _ = #notMacro() } diff --git a/test/Macros/macros_diagnostics.swift b/test/Macros/macros_diagnostics.swift index 22a4c0d400df2..28e3e48488a52 100644 --- a/test/Macros/macros_diagnostics.swift +++ b/test/Macros/macros_diagnostics.swift @@ -132,11 +132,11 @@ func testDiags(a: Int, b: Int) { func shadow(a: Int, b: Int, stringify: Int) { _ = #stringify(a + b) - // expected-error@-1{{external macro implementation type 'MacroDefinition.StringifyMacro' could not be found for macro 'stringify'}} + // expected-error@-1{{external macro implementation type 'MacroDefinition.StringifyMacro' could not be found for macro 'stringify'; plugin for module 'MacroDefinition' not found}} } func testMissing() { - #missingMacro1("hello") // expected-error{{external macro implementation type 'MissingModule.MissingType' could not be found for macro 'missingMacro1'}} + #missingMacro1("hello") // expected-error{{external macro implementation type 'MissingModule.MissingType' could not be found for macro 'missingMacro1'; plugin for module 'MissingModule' not found}} } @freestanding(expression) macro undefined() // expected-error{{macro 'undefined()' requires a definition}} @@ -148,12 +148,12 @@ func testExternalMacroOutOfPlace() { @freestanding(expression) public macro macroWithDefaults(_: Int = 17) = #externalMacro(module: "A", type: "B") -// expected-warning@-1{{external macro implementation type 'A.B' could not be found for macro 'macroWithDefaults'}} +// expected-warning@-1{{external macro implementation type 'A.B' could not be found for macro 'macroWithDefaults'; plugin for module 'A' not found}} // expected-note@-2{{'macroWithDefaults' declared here}} func callMacroWithDefaults() { _ = #macroWithDefaults() - // expected-error@-1 {{external macro implementation type 'A.B' could not be found for macro 'macroWithDefaults'}} + // expected-error@-1 {{external macro implementation type 'A.B' could not be found for macro 'macroWithDefaults'; plugin for module 'A' not found}} } // Make sure we don't allow macros to prevent type folding. @@ -164,7 +164,7 @@ public macro MacroOrType() = #externalMacro(module: "A", type: "MacroOrType") @freestanding(codeItem, names: named(foo)) public macro badCodeItemMacro() = #externalMacro(module: "A", type: "B") // expected-error@-2{{'codeItem' macros are not allowed to introduce names}} -// expected-warning@-2{{external macro implementation type 'A.B' could not be found}} +// expected-warning@-2{{external macro implementation type 'A.B' could not be found for macro 'badCodeItemMacro()'; plugin for module 'A' not found}} struct MacroOrType { typealias Nested = Int diff --git a/tools/swift-plugin-server/Sources/swift-plugin-server/swift-plugin-server.swift b/tools/swift-plugin-server/Sources/swift-plugin-server/swift-plugin-server.swift index 7d3a3c89413a2..284a51f7f8a85 100644 --- a/tools/swift-plugin-server/Sources/swift-plugin-server/swift-plugin-server.swift +++ b/tools/swift-plugin-server/Sources/swift-plugin-server/swift-plugin-server.swift @@ -26,8 +26,13 @@ final class SwiftPluginServer { } } + struct LoadedLibraryPlugin { + var libraryPath: String + var handle: UnsafeMutableRawPointer + } + /// Loaded dylib handles associated with the module name. - var loadedLibraryPlugins: [String: UnsafeMutableRawPointer] = [:] + var loadedLibraryPlugins: [String: LoadedLibraryPlugin] = [:] /// Resolved cached macros. var resolvedMacros: [MacroRef: Macro.Type] = [:] @@ -48,35 +53,39 @@ extension SwiftPluginServer: PluginProvider { func loadPluginLibrary(libraryPath: String, moduleName: String) throws { var errorMessage: UnsafePointer? guard let dlHandle = PluginServer_load(libraryPath, &errorMessage) else { - throw PluginServerError(message: String(cString: errorMessage!)) + throw PluginServerError(message: "loader error: " + String(cString: errorMessage!)) } - loadedLibraryPlugins[moduleName] = dlHandle + loadedLibraryPlugins[moduleName] = LoadedLibraryPlugin( + libraryPath: libraryPath, + handle: dlHandle + ) } /// Lookup a loaded macro by a pair of module name and type name. - func resolveMacro(moduleName: String, typeName: String) -> Macro.Type? { + func resolveMacro(moduleName: String, typeName: String) throws -> Macro.Type { if let resolved = resolvedMacros[.init(moduleName, typeName)] { return resolved } // Find 'dlopen'ed library for the module name. - guard let dlHandle = loadedLibraryPlugins[moduleName] else { - return nil + guard let plugin = loadedLibraryPlugins[moduleName] else { + // NOTE: This should be unreachable. Compiler should not use this server + // unless the plugin loading succeeded. + throw PluginServerError(message: "(plugin-server) plugin not loaded for module '\(moduleName)'") } // Lookup the type metadata. var errorMessage: UnsafePointer? guard let macroTypePtr = PluginServer_lookupMacroTypeMetadataByExternalName( - moduleName, typeName, dlHandle, &errorMessage + moduleName, typeName, plugin.handle, &errorMessage ) else { - // FIXME: Propagate error message? - return nil + throw PluginServerError(message: "macro implementation type '\(moduleName).\(typeName)' could not be found in library plugin '\(plugin.libraryPath)'") } // THe type must be a 'Macro' type. let macroType = unsafeBitCast(macroTypePtr, to: Any.Type.self) guard let macro = macroType as? Macro.Type else { - return nil + throw PluginServerError(message: "type '\(moduleName).\(typeName)' is not a valid macro implementation type in library plugin '\(plugin.libraryPath)'") } // Cache the resolved type.