https://github.com/jthackray updated https://github.com/llvm/llvm-project/pull/202919
>From d988b381e3461880d5b3b62b524d0de3aea75291 Mon Sep 17 00:00:00 2001 From: Jonathan Thackray <[email protected]> Date: Wed, 10 Jun 2026 11:31:05 +0100 Subject: [PATCH 1/2] [clang][Driver] Include stdc-predef.h by default on musl Match GCC behavior for musl-based Linux targets by implicitly passing `-include stdc-predef.h` for non-freestanding compilations. Unlike glibc, musl does not arrange for this header to be included through its normal system headers. As a result, Clang may miss predefined C macros provided by musl's stdc-predef.h unless users include it manually, while the same code works with GCC. Add Driver tests covering musl, non-musl, freestanding, preprocessed input, missing-header diagnostics, and actual macro visibility. This patch is based on: https://reviews.llvm.org/D137043 and slightly tweaks it, as the code has drifted since 2023 when that was authored. Co-authored-by: Tao Liang <[email protected]> Co-authored-by: YingChi Long <[email protected]> --- clang/docs/ReleaseNotes.rst | 3 + clang/lib/Driver/ToolChains/Linux.cpp | 9 ++ .../Driver/Inputs/stdc-predef/preprocessed.i | 1 + .../stdc-predef/usr/include/stdc-predef.h | 4 + clang/test/Driver/stdc-predef.c | 95 +++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 clang/test/Driver/Inputs/stdc-predef/preprocessed.i create mode 100644 clang/test/Driver/Inputs/stdc-predef/usr/include/stdc-predef.h create mode 100644 clang/test/Driver/stdc-predef.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 7828135a6edbc..f28f0bea048e5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -334,6 +334,9 @@ Non-comprehensive list of changes in this release and ``__builtin___get_unsafe_stack_start`` are now deprecated. Use the corresponding functions from ``<sanitizer/safestack_interface.h>`` instead. +- Clang now implicitly includes ``stdc-predef.h`` for non-freestanding + compilations targeting musl-based Linux systems, matching GCC behavior. + New Compiler Flags ------------------ - New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index 512788d235fec..6709638fc7c28 100644 --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -825,6 +825,15 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, if (!DriverArgs.hasArg(options::OPT_nobuiltininc) && getTriple().isMusl()) addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude); + + // For GCC compatibility, add an implicit include for musl-based + // non-freestanding systems. + if (getTriple().isMusl() && + !DriverArgs.hasFlag(options::OPT_ffreestanding, options::OPT_fhosted, + false)) { + CC1Args.push_back("-include"); + CC1Args.push_back("stdc-predef.h"); + } } void Linux::addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs, diff --git a/clang/test/Driver/Inputs/stdc-predef/preprocessed.i b/clang/test/Driver/Inputs/stdc-predef/preprocessed.i new file mode 100644 index 0000000000000..048f715b4656f --- /dev/null +++ b/clang/test/Driver/Inputs/stdc-predef/preprocessed.i @@ -0,0 +1 @@ +int i; diff --git a/clang/test/Driver/Inputs/stdc-predef/usr/include/stdc-predef.h b/clang/test/Driver/Inputs/stdc-predef/usr/include/stdc-predef.h new file mode 100644 index 0000000000000..0a8bef6e196ea --- /dev/null +++ b/clang/test/Driver/Inputs/stdc-predef/usr/include/stdc-predef.h @@ -0,0 +1,4 @@ +#ifndef _STDC_PREDEF_H +#define _STDC_PREDEF_H 1 +#define DUMMY_STDC_PREDEF 1 +#endif diff --git a/clang/test/Driver/stdc-predef.c b/clang/test/Driver/stdc-predef.c new file mode 100644 index 0000000000000..fd8d526eb817e --- /dev/null +++ b/clang/test/Driver/stdc-predef.c @@ -0,0 +1,95 @@ +// Test that clang preincludes stdc-predef.h if we are using a libc that does +// not pre-include it, e.g. musl. + +// Musl-based systems need this additional include. +// +// RUN: %clang %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck -check-prefix CHECK-CPP-FLAG %s + +// GNU-based systems do not need this additional include. +// +// RUN: %clang %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-gnu \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s + +// Freestanding compilations do not need this additional include. +// +// RUN: %clang %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl -ffreestanding \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s + +// The last of -ffreestanding and -fhosted determines whether this additional +// include is needed. +// +// RUN: %clang %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl -ffreestanding -fhosted \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck -check-prefix CHECK-CPP-FLAG %s +// +// RUN: %clang %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl -fhosted -ffreestanding \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s + +// Disabling standard include directories also disables this additional include. +// +// RUN: %clang %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl -nostdinc \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s +// +// RUN: %clang %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl -nostdlibinc \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s + +// Because this behavior is implemented by adding preprocessor flags in the +// driver, disabling preprocessing means the include flag should not appear. +// +// RUN: %clang -x cpp-output %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s + +// The automatic preinclude of stdc-predef.h should not occur if +// the source filename indicates a preprocessed file. +// +// RUN: %clang %S/Inputs/stdc-predef/preprocessed.i -### -c 2>&1 \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: -target x86_64-unknown-linux-musl \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s + +// This behavior should appear in all files clang accepts that use the C +// preprocessor, including C, C++ and Objective-C files, as long as the system +// uses musl. +// +// RUN: %clang -x objective-c %s -### -c 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl \ +// RUN: --sysroot=%S/Inputs/stdc-predef \ +// RUN: | FileCheck -check-prefix CHECK-CPP-FLAG %s + +// If a musl-based system does not have this header, give an error at line 1. +// +// RUN: %clang %s -c -Xclang -verify=expected 2>&1 \ +// RUN: -target x86_64-unknown-linux-musl \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree +// expected-error@1 {{'stdc-predef.h' file not found}} + +// Check if the file is really included by macro. +// +// RUN: %clang %s -c -Xclang -verify=ok -DCHECK_DUMMY=1 \ +// RUN: -target x86_64-unknown-linux-musl \ +// RUN: --sysroot=%S/Inputs/stdc-predef +// ok-no-diagnostics + +// CHECK-CPP-FLAG: "-include" "stdc-predef.h" +int i; +#if CHECK_DUMMY +#if !DUMMY_STDC_PREDEF +#error "Expected macro symbol DUMMY_STDC_PREDEF is not defined." +#endif +#endif >From 2fd832d0115539f7e31b4a7779b0bfbe52b45c00 Mon Sep 17 00:00:00 2001 From: Jonathan Thackray <[email protected]> Date: Tue, 16 Jun 2026 10:52:43 +0100 Subject: [PATCH 2/2] fixup! Address PR comments --- clang/docs/ReleaseNotes.rst | 3 +- clang/lib/Driver/ToolChains/Linux.cpp | 15 +++- clang/test/Driver/stdc-predef.c | 106 +++++++++++--------------- 3 files changed, 57 insertions(+), 67 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index f28f0bea048e5..2136cda29d3ca 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -335,7 +335,8 @@ Non-comprehensive list of changes in this release corresponding functions from ``<sanitizer/safestack_interface.h>`` instead. - Clang now implicitly includes ``stdc-predef.h`` for non-freestanding - compilations targeting musl-based Linux systems, matching GCC behavior. + compilations targeting musl Linux systems, and for glibc Linux sysroots + that provide the header, matching GCC behavior. New Compiler Flags ------------------ diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index 6709638fc7c28..38b8f8a943ad7 100644 --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -826,11 +826,18 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, if (!DriverArgs.hasArg(options::OPT_nobuiltininc) && getTriple().isMusl()) addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude); - // For GCC compatibility, add an implicit include for musl-based - // non-freestanding systems. - if (getTriple().isMusl() && + // For GCC compatibility, add an implicit include for musl-based or + // non-freestanding GNU systems where the target libc provides stdc-predef.h. + bool HasGlibcStdcPredef = + getTriple().isGNUEnvironment() && !SysRoot.empty() && + D.getVFS().exists(concat(SysRoot, "/usr/include/stdc-predef.h")); + + bool ShouldPreincludeStdcPredef = + (getTriple().isMusl() || HasGlibcStdcPredef) && !DriverArgs.hasFlag(options::OPT_ffreestanding, options::OPT_fhosted, - false)) { + false); + + if (ShouldPreincludeStdcPredef) { CC1Args.push_back("-include"); CC1Args.push_back("stdc-predef.h"); } diff --git a/clang/test/Driver/stdc-predef.c b/clang/test/Driver/stdc-predef.c index fd8d526eb817e..f1f116393cd7d 100644 --- a/clang/test/Driver/stdc-predef.c +++ b/clang/test/Driver/stdc-predef.c @@ -1,89 +1,71 @@ -// Test that clang preincludes stdc-predef.h if we are using a libc that does -// not pre-include it, e.g. musl. +// Test that clang preincludes stdc-predef.h for musl Linux targets and +// glibc Linux sysroots that provide the header. // Musl-based systems need this additional include. -// -// RUN: %clang %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck -check-prefix CHECK-CPP-FLAG %s +// RUN: %clang %s -### -c --target=x86_64-unknown-linux-musl \ +// RUN: --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-CPP-FLAG %s -// GNU-based systems do not need this additional include. -// -// RUN: %clang %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-gnu \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s +// GNU-based sysroots with stdc-predef.h also need this additional include. +// RUN: %clang %s -### -c --target=x86_64-unknown-linux-gnu \ +// RUN: --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-CPP-FLAG %s + +// GNU-based sysroots without stdc-predef.h should not inject a host header. +// RUN: %clang %s -### -c --target=x86_64-unknown-linux-gnu \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s // Freestanding compilations do not need this additional include. -// -// RUN: %clang %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl -ffreestanding \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s +// RUN: %clang %s -### -c --target=x86_64-unknown-linux-musl -ffreestanding \ +// RUN: --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s // The last of -ffreestanding and -fhosted determines whether this additional // include is needed. -// -// RUN: %clang %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl -ffreestanding -fhosted \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck -check-prefix CHECK-CPP-FLAG %s -// -// RUN: %clang %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl -fhosted -ffreestanding \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s +// RUN: %clang %s -### -c --target=x86_64-unknown-linux-musl \ +// RUN: -ffreestanding -fhosted --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-CPP-FLAG %s +// RUN: %clang %s -### -c --target=x86_64-unknown-linux-musl \ +// RUN: -fhosted -ffreestanding --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s // Disabling standard include directories also disables this additional include. -// -// RUN: %clang %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl -nostdinc \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s -// -// RUN: %clang %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl -nostdlibinc \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s +// RUN: %clang %s -### -c --target=x86_64-unknown-linux-musl -nostdinc \ +// RUN: --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s +// RUN: %clang %s -### -c --target=x86_64-unknown-linux-musl -nostdlibinc \ +// RUN: --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s // Because this behavior is implemented by adding preprocessor flags in the // driver, disabling preprocessing means the include flag should not appear. -// -// RUN: %clang -x cpp-output %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s +// RUN: %clang -x cpp-output %s -### -c --target=x86_64-unknown-linux-musl \ +// RUN: --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s // The automatic preinclude of stdc-predef.h should not occur if // the source filename indicates a preprocessed file. -// -// RUN: %clang %S/Inputs/stdc-predef/preprocessed.i -### -c 2>&1 \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: -target x86_64-unknown-linux-musl \ -// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s +// RUN: %clang %S/Inputs/stdc-predef/preprocessed.i -### -c \ +// RUN: --target=x86_64-unknown-linux-musl --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --implicit-check-not "stdc-predef.h" %s // This behavior should appear in all files clang accepts that use the C // preprocessor, including C, C++ and Objective-C files, as long as the system -// uses musl. -// -// RUN: %clang -x objective-c %s -### -c 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl \ -// RUN: --sysroot=%S/Inputs/stdc-predef \ -// RUN: | FileCheck -check-prefix CHECK-CPP-FLAG %s +// uses musl or a glibc sysroot that provides the header. +// RUN: %clang -x objective-c %s -### -c \ +// RUN: --target=x86_64-unknown-linux-musl --sysroot=%S/Inputs/stdc-predef 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-CPP-FLAG %s -// If a musl-based system does not have this header, give an error at line 1. -// -// RUN: %clang %s -c -Xclang -verify=expected 2>&1 \ -// RUN: -target x86_64-unknown-linux-musl \ -// RUN: --sysroot=%S/Inputs/basic_linux_tree +// If a target system does not have this header, give an error at line 1. +// RUN: %clang %s -c -Xclang -verify=expected \ +// RUN: --target=x86_64-unknown-linux-musl \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 // expected-error@1 {{'stdc-predef.h' file not found}} // Check if the file is really included by macro. -// // RUN: %clang %s -c -Xclang -verify=ok -DCHECK_DUMMY=1 \ -// RUN: -target x86_64-unknown-linux-musl \ -// RUN: --sysroot=%S/Inputs/stdc-predef +// RUN: --target=x86_64-unknown-linux-musl --sysroot=%S/Inputs/stdc-predef // ok-no-diagnostics // CHECK-CPP-FLAG: "-include" "stdc-predef.h" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
