https://github.com/matrix updated https://github.com/llvm/llvm-project/pull/229781
>From 110fe0af4d0fd463e7d82202bc95980602598fd3 Mon Sep 17 00:00:00 2001 From: Gabriele Gristina <[email protected]> Date: Wed, 7 Oct 2026 16:37:45 +0200 Subject: [PATCH] [clang] Report an invalid -mllvm or -mmlir argument instead of exiting An option passed through -mllvm reaches llvm::cl::ParseCommandLineOptions, which prints to llvm::errs() and calls exit() when it is not given a stream to report to. clang passes nullptr, so an invalid option ends the process from inside the option parser. That is fatal for anything that embeds clang as a library, hiprtc among others: one invalid -mllvm option ends the host process rather than failing the compilation it belongs to, and a caller cannot test whether an option exists, because asking is what kills it. Pass llvm::errs(), the stream the parser falls back to when it is given none, so the text it writes for an unknown argument is unchanged, and report the failure it returns as a diagnostic. ExecuteCompilerInvocation already returns false once the error count has grown, and cc1as already skips the assembler once a diagnostic has been reported, so the invocation fails the ordinary way and the caller keeps control. Three call sites forward options to a cl:: parser this way and all three pass nullptr, so all three are changed: -mllvm and -mmlir in ExecuteCompilerInvocation, and -mllvm in cc1as_main.cpp. The diagnostic names the flag the option came from. The -mmlir one is compiled only when CLANG_ENABLE_CIR is on, so clang/test/CIR/mlirargs.c covers it only in that configuration. --- clang/docs/ReleaseNotes.md | 2 ++ .../clang/Basic/DiagnosticFrontendKinds.td | 2 ++ .../ExecuteCompilerInvocation.cpp | 21 +++++++++++++------ clang/test/CIR/mlirargs.c | 1 + clang/test/Driver/unknown-arg.c | 4 ++++ clang/test/Frontend/unknown-arg.c | 7 +++++++ clang/tools/driver/cc1as_main.cpp | 8 +++++-- 7 files changed, 37 insertions(+), 8 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index efbcc198a28e3..dd64034413cf1 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -882,6 +882,8 @@ features cannot lower the translation-unit ABI level; #### Miscellaneous Bug Fixes +- Fixed clang terminating the process from inside the LLVM or MLIR option parser, instead of reporting a diagnostic, when an invalid option was passed through `-mllvm` or `-mmlir`. + #### Miscellaneous Clang Crashes Fixed - Fixed a crash in CTAD for type alias templates when the aggregate deduction guide could not be resolved. (#GH206994) diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 232475b6fb5a7..0ab712b4e9374 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -153,6 +153,8 @@ def err_cir_input_missing_triple : Error< "missing target triple in ClangIR input">; def err_cir_input_triple_mismatch : Error< "target triple '%0' of ClangIR input does not match the target '%1'">; +def err_fe_invalid_forwarded_option : Error< + "invalid argument in '%select{-mllvm|-mmlir}0'">; def err_fe_invalid_multiple_actions : Error< "'%0' action ignored; '%1' action specified previously">; def err_fe_invalid_alignment : Error< diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index e872f0823f23e..5077115c2e878 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -34,6 +34,7 @@ #include "llvm/Support/BuryPointer.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" #if CLANG_ENABLE_CIR #include "mlir/IR/AsmState.h" @@ -264,9 +265,13 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) { for (unsigned i = 0; i != NumArgs; ++i) Args[i + 1] = Clang->getFrontendOpts().LLVMArgs[i].c_str(); Args[NumArgs + 1] = nullptr; - llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(), /*Overview=*/"", - /*Errs=*/nullptr, - /*VFS=*/&Clang->getVirtualFileSystem()); + // With no stream to report to, the parser calls exit(), which ends the + // process that embeds clang. Keep its output where it already went. + if (!llvm::cl::ParseCommandLineOptions( + NumArgs + 1, Args.get(), /*Overview=*/"", /*Errs=*/&llvm::errs(), + /*VFS=*/&Clang->getVirtualFileSystem())) + Clang->getDiagnostics().Report(diag::err_fe_invalid_forwarded_option) + << 0; } #if CLANG_ENABLE_STATIC_ANALYZER @@ -313,9 +318,13 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) { for (unsigned i = 0; i != NumArgs; ++i) Args[i + 1] = Clang->getFrontendOpts().MLIRArgs[i].c_str(); Args[NumArgs + 1] = nullptr; - llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(), - /*Description=*/"", /*Errs=*/nullptr, - &Clang->getVirtualFileSystem()); + // With no stream to report to, the parser calls exit(), which ends the + // process that embeds clang. Keep its output where it already went. + if (!llvm::cl::ParseCommandLineOptions( + NumArgs + 1, Args.get(), /*Overview=*/"", /*Errs=*/&llvm::errs(), + /*VFS=*/&Clang->getVirtualFileSystem())) + Clang->getDiagnostics().Report(diag::err_fe_invalid_forwarded_option) + << 1; } #endif diff --git a/clang/test/CIR/mlirargs.c b/clang/test/CIR/mlirargs.c index cfb07197ef185..b28c7c59e3797 100644 --- a/clang/test/CIR/mlirargs.c +++ b/clang/test/CIR/mlirargs.c @@ -8,5 +8,6 @@ // WRONG: clang (MLIR option parsing): Did you mean '--mlir-disable-threading'? // WRONG: clang (MLIR option parsing): Unknown command line argument '-mlir-print-op-genericd'. Try: 'clang (MLIR option parsing) --help' // WRONG: clang (MLIR option parsing): Did you mean '--mlir-print-op-generic'? +// WRONG: error: invalid argument in '-mmlir' // CC1: "-mmlir" "-mlir-disable-threading" diff --git a/clang/test/Driver/unknown-arg.c b/clang/test/Driver/unknown-arg.c index 26032a407f413..9bddef95d5fa0 100644 --- a/clang/test/Driver/unknown-arg.c +++ b/clang/test/Driver/unknown-arg.c @@ -20,6 +20,8 @@ // RUN: FileCheck %s --check-prefix=SILENT // RUN: not %clang -cc1as -hell --version 2>&1 | \ // RUN: FileCheck %s --check-prefix=CC1AS-DID-YOU-MEAN +// RUN: not %clang -cc1as -mllvm -helium 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CC1AS-MLLVM // RUN: not %clang -cc1asphalt -help 2>&1 | \ // RUN: FileCheck %s --check-prefix=UNKNOWN-INTEGRATED @@ -59,6 +61,8 @@ // SILENT-NOT: warning: // CC1AS-DID-YOU-MEAN: error: unknown argument '-hell'; did you mean '-help'? // CC1AS-DID-YOU-MEAN: error: unknown argument '--version'; did you mean '-version'? +// CC1AS-MLLVM: clang (LLVM option parsing): Unknown command line argument '-helium' +// CC1AS-MLLVM: error: invalid argument in '-mllvm' // UNKNOWN-INTEGRATED: error: unknown integrated tool '-cc1asphalt'. Valid tools include '-cc1', '-cc1as' and '-cc1gen-reproducer'. // RUN: %clang -S %s -o %t.s -Wunknown-to-clang-option 2>&1 | FileCheck --check-prefix=IGNORED %s diff --git a/clang/test/Frontend/unknown-arg.c b/clang/test/Frontend/unknown-arg.c index 74a6c842da908..9cebe195c2413 100644 --- a/clang/test/Frontend/unknown-arg.c +++ b/clang/test/Frontend/unknown-arg.c @@ -6,7 +6,14 @@ // RUN: FileCheck %s --check-prefix=DID-YOU-MEAN // RUN: not %clang_cc1 --version 2>&1 | \ // RUN: FileCheck %s --check-prefix=DID-YOU-MEAN-VER +// RUN: not %clang_cc1 %s -E -mllvm -helium 2>&1 | \ +// RUN: FileCheck %s --check-prefix=MLLVM // CHECK: error: unknown argument: '--helium' // DID-YOU-MEAN: error: unknown argument '--hel['; did you mean '--help'? // DID-YOU-MEAN-VER: error: unknown argument '--version'; did you mean '-version'? + +// An unknown option behind -mllvm is reported, and the text LLVM's own parser +// writes is unchanged, because it still goes to the stream it used before. +// MLLVM: clang (LLVM option parsing): Unknown command line argument '-helium' +// MLLVM: error: invalid argument in '-mllvm' diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index 077cd69ce4e2c..41f1562208a5d 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -731,8 +731,12 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { for (unsigned i = 0; i != NumArgs; ++i) Args[i + 1] = Asm.LLVMArgs[i].c_str(); Args[NumArgs + 1] = nullptr; - llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(), /*Overview=*/"", - /*Errs=*/nullptr, /*VFS=*/VFS.get()); + // With no stream to report to, the parser calls exit(), which ends the + // process that embeds clang. Keep its output where it already went. + if (!llvm::cl::ParseCommandLineOptions( + NumArgs + 1, Args.get(), /*Overview=*/"", /*Errs=*/&llvm::errs(), + /*VFS=*/VFS.get())) + Diags.Report(diag::err_fe_invalid_forwarded_option) << 0; } // Execute the invocation, unless there were parsing errors. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
