Index: include/clang/Basic/Sanitizers.h =================================================================== --- include/clang/Basic/Sanitizers.h +++ include/clang/Basic/Sanitizers.h @@ -61,8 +61,9 @@ Mask = Value ? (Mask | K) : (Mask & ~K); } - /// \brief Disable all sanitizers. - void clear() { Mask = 0; } + /// \brief Disable the sanitizers specified in \p K (by default, disable + /// all sanitizers). + void clear(SanitizerMask K = SanitizerKind::All) { Mask &= ~K; } /// \brief Returns true if at least one sanitizer is enabled. bool empty() const { return Mask == 0; } Index: lib/Basic/LangOptions.cpp =================================================================== --- lib/Basic/LangOptions.cpp +++ lib/Basic/LangOptions.cpp @@ -29,9 +29,7 @@ Name = Default; #include "clang/Basic/LangOptions.def" - // FIXME: This should not be reset; modules can be different with different - // sanitizer options (this affects __has_feature(address_sanitizer) etc). - Sanitize.clear(); + // These options do not affect AST generation. SanitizerBlacklistFiles.clear(); XRayAlwaysInstrumentFiles.clear(); XRayNeverInstrumentFiles.clear(); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2693,6 +2693,14 @@ code = ext->hashExtension(code); } + // Extend the signature with the enabled sanitizers, if at least one is + // enabled. Sanitizers which cannot affect AST generation aren't hashed. + SanitizerSet SanHash = LangOpts->Sanitize; + SanHash.clear(SanitizerKind::CFI | SanitizerKind::Integer | + SanitizerKind::Nullability | SanitizerKind::Undefined); + if (!SanHash.empty()) + code = hash_combine(code, SanHash.Mask); + return llvm::APInt(64, code).toString(36, /*Signed=*/false); } Index: test/Modules/Inputs/check-for-sanitizer-feature/check.h =================================================================== --- /dev/null +++ test/Modules/Inputs/check-for-sanitizer-feature/check.h @@ -0,0 +1,5 @@ +#if __has_feature(address_sanitizer) +#define HAS_ASAN 1 +#else +#define HAS_ASAN 0 +#endif Index: test/Modules/Inputs/check-for-sanitizer-feature/map =================================================================== --- /dev/null +++ test/Modules/Inputs/check-for-sanitizer-feature/map @@ -0,0 +1,3 @@ +module check_feature { + header "check.h" +} Index: test/Modules/check-for-sanitizer-feature.cpp =================================================================== --- /dev/null +++ test/Modules/check-for-sanitizer-feature.cpp @@ -0,0 +1,50 @@ +// RUN: rm -rf %t.1 %t.2 +// RUN: mkdir %t.1 %t.2 + +// Build and use an ASan-enabled module. +// RUN: %clang_cc1 -fsanitize=address -fmodules -fmodules-cache-path=%t.1 \ +// RUN: -fmodule-map-file=%S/Inputs/check-for-sanitizer-feature/map \ +// RUN: -I %S/Inputs/check-for-sanitizer-feature -verify %s +// RUN: ls %t.1 | count 2 + +// Force a module rebuild by disabling ASan. +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t.1 \ +// RUN: -fmodule-map-file=%S/Inputs/check-for-sanitizer-feature/map \ +// RUN: -I %S/Inputs/check-for-sanitizer-feature -verify %s +// RUN: ls %t.1 | count 3 + +// Enable ASan again: check that there is no import failure, and no rebuild. +// RUN: %clang_cc1 -fsanitize=address -fmodules -fmodules-cache-path=%t.1 \ +// RUN: -fmodule-map-file=%S/Inputs/check-for-sanitizer-feature/map \ +// RUN: -I %S/Inputs/check-for-sanitizer-feature -verify %s +// RUN: ls %t.1 | count 3 + +// Some sanitizers can not affect AST generation when enabled. Check that +// module rebuilds don't occur when these sanitizers are enabled. +// +// First, built without any sanitization. +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t.2 \ +// RUN: -fmodule-map-file=%S/Inputs/check-for-sanitizer-feature/map \ +// RUN: -I %S/Inputs/check-for-sanitizer-feature -verify %s +// RUN: ls %t.2 | count 2 +// +// Next, build with sanitization, and check that a new module isn't built. +// RUN: %clang_cc1 -fsanitize=cfi-vcall,unsigned-integer-overflow,nullability-arg,null -fmodules \ +// RUN: -fmodules-cache-path=%t.2 \ +// RUN: -fmodule-map-file=%S/Inputs/check-for-sanitizer-feature/map \ +// RUN: -I %S/Inputs/check-for-sanitizer-feature -verify %s +// RUN: ls %t.2 | count 2 + +#include "check.h" + +#if __has_feature(address_sanitizer) +#if HAS_ASAN != 1 +#error Does not have the address_sanitizer feature, but should. +#endif +#else +#if HAS_ASAN != 0 +#error Has the address_sanitizer feature, but shouldn't. +#endif +#endif + +// expected-no-diagnostics