Skip to content

[Support] Treat Windows "nul" as the null device in writeToOutput#208179

Open
wenju-he wants to merge 2 commits into
llvm:mainfrom
wenju-he:raw_ostream-nul-windows
Open

[Support] Treat Windows "nul" as the null device in writeToOutput#208179
wenju-he wants to merge 2 commits into
llvm:mainfrom
wenju-he:raw_ostream-nul-windows

Conversation

@wenju-he

@wenju-he wenju-he commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

llvm-objcopy (via writeToOutput) only special-cased "/dev/null", so on Windows an output path of "nul" fell through to TempFile::create() + rename. Windows reserves "nul" as a device name, so the rename fails with "permission denied". This broke clang-offload-bundler's SYCL fat object step when the driver is invoked with -o nul in downstream test https://github.com/intel/llvm/blob/sycl/sycl/test/include_deps/header_reach.cpp

Assisted by: Claude

llvm-objcopy (via writeToOutput) only special-cased "/dev/null", so on
Windows an output path of "nul" fell through to TempFile::create() +
rename. Windows reserves "nul" as a device name, so the rename fails
with "permission denied". This broke clang-offload-bundler's SYCL fat
object step when the driver is invoked with `-o nul` in downstream test
https://github.com/intel/llvm/blob/sycl/sycl/test/include_deps/header_reach.cpp

Assisted by: Claude
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff origin/main HEAD --extensions cpp -- llvm/lib/Support/raw_ostream.cpp llvm/unittests/Support/raw_ostream_test.cpp --diff_from_common_commit

⚠️
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing origin/main to the base branch/commit you want to compare against.
⚠️

View the diff from clang-format here.
diff --git a/llvm/unittests/Support/raw_ostream_test.cpp b/llvm/unittests/Support/raw_ostream_test.cpp
index ed059faec..c7cb9e340 100644
--- a/llvm/unittests/Support/raw_ostream_test.cpp
+++ b/llvm/unittests/Support/raw_ostream_test.cpp
@@ -636,8 +636,9 @@ TEST(raw_ostreamTest, writeToNul) {
       writeToOutput("nul",
                     [&](raw_ostream &Out) -> Error {
                       std::error_code EC;
-                      sys::fs::directory_iterator It(
-                          RootTestDirectory.path(), EC), End;
+                      sys::fs::directory_iterator It(RootTestDirectory.path(),
+                                                     EC),
+                          End;
                       EXPECT_FALSE(EC);
                       EXPECT_EQ(It, End)
                           << "unexpected temp file on disk during Write(): "

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-support

Author: Wenju He (wenju-he)

Changes

llvm-objcopy (via writeToOutput) only special-cased "/dev/null", so on Windows an output path of "nul" fell through to TempFile::create() + rename. Windows reserves "nul" as a device name, so the rename fails with "permission denied". This broke clang-offload-bundler's SYCL fat object step when the driver is invoked with -o nul in downstream test https://github.com/intel/llvm/blob/sycl/sycl/test/include_deps/header_reach.cpp

Assisted by: Claude


Full diff: https://github.com/llvm/llvm-project/pull/208179.diff

2 Files Affected:

  • (modified) llvm/lib/Support/raw_ostream.cpp (+9-1)
  • (modified) llvm/unittests/Support/raw_ostream_test.cpp (+29)
diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index eebc4db5ac878..8bc00dc07d522 100644
--- a/llvm/lib/Support/raw_ostream.cpp
+++ b/llvm/lib/Support/raw_ostream.cpp
@@ -993,12 +993,20 @@ void buffer_ostream::anchor() {}
 
 void buffer_unique_ostream::anchor() {}
 
+static bool isNullDeviceName(StringRef OutputFileName) {
+#ifdef _WIN32
+  if (OutputFileName.equals_insensitive("nul"))
+    return true;
+#endif
+  return OutputFileName == "/dev/null";
+}
+
 Error llvm::writeToOutput(StringRef OutputFileName,
                           std::function<Error(raw_ostream &)> Write) {
   if (OutputFileName == "-")
     return Write(outs());
 
-  if (OutputFileName == "/dev/null") {
+  if (isNullDeviceName(OutputFileName)) {
     raw_null_ostream Out;
     return Write(Out);
   }
diff --git a/llvm/unittests/Support/raw_ostream_test.cpp b/llvm/unittests/Support/raw_ostream_test.cpp
index ed04721816476..c7cb9e340dbcf 100644
--- a/llvm/unittests/Support/raw_ostream_test.cpp
+++ b/llvm/unittests/Support/raw_ostream_test.cpp
@@ -623,6 +623,35 @@ TEST(raw_ostreamTest, writeToDevNull) {
   EXPECT_TRUE(DevNullIsUsed);
 }
 
+#ifdef _WIN32
+TEST(raw_ostreamTest, writeToNul) {
+  // raw_null_ostream never touches the filesystem, so no "nul.temp-stream-*"
+  // file should exist in the CWD while Write() runs.
+  llvm::unittest::TempDir RootTestDirectory("writeToNul", /*Unique=*/true);
+  SmallString<128> SavedCWD;
+  ASSERT_FALSE(sys::fs::current_path(SavedCWD));
+  ASSERT_FALSE(sys::fs::set_current_path(RootTestDirectory.path()));
+
+  EXPECT_THAT_ERROR(
+      writeToOutput("nul",
+                    [&](raw_ostream &Out) -> Error {
+                      std::error_code EC;
+                      sys::fs::directory_iterator It(RootTestDirectory.path(),
+                                                     EC),
+                          End;
+                      EXPECT_FALSE(EC);
+                      EXPECT_EQ(It, End)
+                          << "unexpected temp file on disk during Write(): "
+                          << It->path();
+                      Out << "HelloWorld";
+                      return Error::success();
+                    }),
+      Succeeded());
+
+  ASSERT_FALSE(sys::fs::set_current_path(SavedCWD));
+}
+#endif
+
 TEST(raw_ostreamTest, nullStreamZeroBufferSize) {
   raw_ostream &NullStream = nulls();
   EXPECT_EQ(NullStream.GetBufferSize(), 0u);

@wenju-he wenju-he requested a review from MaskRay July 8, 2026 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant