summaryrefslogtreecommitdiff
path: root/clang/test/Modules
diff options
context:
space:
mode:
Diffstat (limited to 'clang/test/Modules')
-rw-r--r--clang/test/Modules/GH154840.cpp97
-rw-r--r--clang/test/Modules/GH155028-1.cpp17
-rw-r--r--clang/test/Modules/Inputs/umbrella_header_order/module.modulemap3
-rw-r--r--clang/test/Modules/Inputs/umbrella_header_order/umbrella/A.h0
-rw-r--r--clang/test/Modules/Inputs/umbrella_header_order/umbrella/B.h0
-rw-r--r--clang/test/Modules/Inputs/umbrella_header_order/umbrella/C.h0
-rw-r--r--clang/test/Modules/Inputs/umbrella_header_order/umbrella/D.h0
-rw-r--r--clang/test/Modules/Inputs/umbrella_header_order/umbrella/E.h0
-rw-r--r--clang/test/Modules/Inputs/umbrella_header_order/umbrella/F.h0
-rw-r--r--clang/test/Modules/added-visible-decls.cppm57
-rw-r--r--clang/test/Modules/coro-await-elidable.cppm200
-rw-r--r--clang/test/Modules/crash-vfs-path-symlink-component.m10
-rw-r--r--clang/test/Modules/crash-vfs-path-traversal.m10
-rw-r--r--clang/test/Modules/crash-vfs-relative-overlay.m9
-rw-r--r--clang/test/Modules/implicit-opt-level.c15
-rw-r--r--clang/test/Modules/merge-records.cppm21
-rw-r--r--clang/test/Modules/modules-cache-path-canonicalization-output.c18
-rw-r--r--clang/test/Modules/safe_buffers_optout.cpp4
-rw-r--r--clang/test/Modules/umbrella_dir_order.m11
19 files changed, 443 insertions, 29 deletions
diff --git a/clang/test/Modules/GH154840.cpp b/clang/test/Modules/GH154840.cpp
new file mode 100644
index 000000000000..afeb39acb548
--- /dev/null
+++ b/clang/test/Modules/GH154840.cpp
@@ -0,0 +1,97 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -fmodule-name=A -fno-cxx-modules -emit-module -fmodules -xc++ A.cppmap -o A.pcm
+// RUN: %clang_cc1 -fmodule-name=B -fno-cxx-modules -emit-module -fmodules -xc++ B.cppmap -o B.pcm -fmodule-file=A.pcm
+// RUN: %clang_cc1 -fmodule-name=C -fno-cxx-modules -emit-module -fmodules -xc++ C.cppmap -o C.pcm -fmodule-file=A.pcm
+// RUN: %clang_cc1 -fmodule-name=D -fno-cxx-modules -emit-module -fmodules -xc++ D.cppmap -o D.pcm -fmodule-file=A.pcm
+// RUN: %clang_cc1 -fmodule-name=E -fno-cxx-modules -emit-module -fmodules -xc++ E.cppmap -o E.pcm -fmodule-file=D.pcm -fmodule-file=B.pcm -fmodule-file=C.pcm
+// RUN: %clang_cc1 -fno-cxx-modules -fmodules -fmodule-file=B.pcm -fmodule-file=E.pcm -emit-llvm -o /dev/null S.cpp
+
+//--- A.h
+namespace std {
+
+template <class T> void zz(T);
+
+template <class> struct vec {
+ struct w {};
+ struct xx {};
+
+ vec(vec &) { init(); }
+ constexpr vec &operator=(const vec &);
+ template <class U> constexpr void pb(U);
+ constexpr void init();
+
+ w s;
+};
+
+template <class T> constexpr void vec<T>::init() {
+ xx yy;
+ zz(yy);
+}
+
+template <class T> constexpr vec<T> &vec<T>::operator=(const vec &) {
+ pb(s);
+ return *this;
+}
+
+template <class T> template <class U> constexpr void vec<T>::pb(U) { init(); }
+} // namespace std
+
+//--- A.cppmap
+module "A" {
+ header "A.h"
+}
+
+//--- X.h
+#pragma clang module import A
+
+namespace project {
+ class thing : std::vec<thing> {};
+} // namespace project
+
+//--- B.h
+#include "X.h"
+
+//--- B.cppmap
+module "B" {
+ header "B.h"
+}
+
+//--- C.h
+#include "X.h"
+
+//--- C.cppmap
+module "C" {
+ header "C.h"
+}
+
+//--- D.h
+#include "X.h"
+
+//--- D.cppmap
+module "D" {
+ header "D.h"
+}
+
+//--- Y.h
+#include "X.h"
+struct other {
+ other() : data(data) {}
+ std::vec<project::thing> data;
+};
+
+//--- E.h
+#include "Y.h"
+
+//--- E.cppmap
+module "E" {
+ header "E.h"
+}
+
+//--- S.cpp
+#pragma clang module import A
+#pragma clang module import E
+void func(std::vec<project::thing> *a, std::vec<project::thing> *b) { *a = *b; }
diff --git a/clang/test/Modules/GH155028-1.cpp b/clang/test/Modules/GH155028-1.cpp
new file mode 100644
index 000000000000..d60112b48c21
--- /dev/null
+++ b/clang/test/Modules/GH155028-1.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+// expected-no-diagnostics
+
+#pragma clang module build M
+module "M" {
+ module "A" {}
+ module "B" {}
+}
+#pragma clang module contents
+#pragma clang module begin M.A
+enum E1 {};
+#pragma clang module end
+#pragma clang module begin M.B
+enum E1 {};
+using T = __underlying_type(E1);
+#pragma clang module end
+#pragma clang module endbuild
diff --git a/clang/test/Modules/Inputs/umbrella_header_order/module.modulemap b/clang/test/Modules/Inputs/umbrella_header_order/module.modulemap
new file mode 100644
index 000000000000..5c64e3306882
--- /dev/null
+++ b/clang/test/Modules/Inputs/umbrella_header_order/module.modulemap
@@ -0,0 +1,3 @@
+module x {
+ umbrella "umbrella"
+} \ No newline at end of file
diff --git a/clang/test/Modules/Inputs/umbrella_header_order/umbrella/A.h b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/A.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/A.h
diff --git a/clang/test/Modules/Inputs/umbrella_header_order/umbrella/B.h b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/B.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/B.h
diff --git a/clang/test/Modules/Inputs/umbrella_header_order/umbrella/C.h b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/C.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/C.h
diff --git a/clang/test/Modules/Inputs/umbrella_header_order/umbrella/D.h b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/D.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/D.h
diff --git a/clang/test/Modules/Inputs/umbrella_header_order/umbrella/E.h b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/E.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/E.h
diff --git a/clang/test/Modules/Inputs/umbrella_header_order/umbrella/F.h b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/F.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/clang/test/Modules/Inputs/umbrella_header_order/umbrella/F.h
diff --git a/clang/test/Modules/added-visible-decls.cppm b/clang/test/Modules/added-visible-decls.cppm
new file mode 100644
index 000000000000..2f387db45290
--- /dev/null
+++ b/clang/test/Modules/added-visible-decls.cppm
@@ -0,0 +1,57 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -emit-reduced-module-interface -o %t/b.pcm -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -emit-reduced-module-interface -o %t/c.pcm -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/d.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
+//--- a.h
+template <typename T>
+struct A {
+ static const T value0;
+ static const T value1;
+
+ constexpr T get0() {
+ return value0;
+ }
+
+ constexpr T get1() {
+ return value1;
+ }
+};
+
+template <typename T>
+const T A<T>::value0 = T(43);
+template <typename T>
+const T A<T>::value1 = T(44);
+
+//--- a.cppm
+module;
+#include "a.h"
+export module a;
+export using ::A;
+
+//--- b.cppm
+export module b;
+export import a;
+
+export constexpr int bar() {
+ return A<int>().get0();
+}
+
+//--- c.cppm
+export module c;
+export import b;
+
+export constexpr int foo() {
+ return A<int>().get1() + A<int>().get0();
+}
+
+//--- d.cpp
+// expected-no-diagnostics
+
+import c;
+
+static_assert(bar() + foo() == 130);
+
diff --git a/clang/test/Modules/coro-await-elidable.cppm b/clang/test/Modules/coro-await-elidable.cppm
new file mode 100644
index 000000000000..2d635c6efa88
--- /dev/null
+++ b/clang/test/Modules/coro-await-elidable.cppm
@@ -0,0 +1,200 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 %t/task.cppm -I%t -emit-reduced-module-interface -o %t/task.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/user.cpp -verify -fsyntax-only
+
+//--- coroutine.h
+
+namespace std {
+
+template <typename R, typename...> struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+};
+
+template <typename Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *addr) noexcept {
+ coroutine_handle me;
+ me.ptr = addr;
+ return me;
+ }
+ void operator()() { resume(); }
+ void *address() const noexcept { return ptr; }
+ void resume() const { __builtin_coro_resume(ptr); }
+ void destroy() const { __builtin_coro_destroy(ptr); }
+ bool done() const { return __builtin_coro_done(ptr); }
+ coroutine_handle &operator=(decltype(nullptr)) {
+ ptr = nullptr;
+ return *this;
+ }
+ coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
+ coroutine_handle() : ptr(nullptr) {}
+// void reset() { ptr = nullptr; } // add to P0057?
+ explicit operator bool() const { return ptr; }
+
+protected:
+ void *ptr;
+};
+
+template <typename Promise> struct coroutine_handle : coroutine_handle<> {
+ using coroutine_handle<>::operator=;
+
+ static coroutine_handle from_address(void *addr) noexcept {
+ coroutine_handle me;
+ me.ptr = addr;
+ return me;
+ }
+
+ Promise &promise() const {
+ return *reinterpret_cast<Promise *>(
+ __builtin_coro_promise(ptr, alignof(Promise), false));
+ }
+ static coroutine_handle from_promise(Promise &promise) {
+ coroutine_handle p;
+ p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
+ return p;
+ }
+};
+
+template <typename _PromiseT>
+bool operator==(coroutine_handle<_PromiseT> const &_Left,
+ coroutine_handle<_PromiseT> const &_Right) noexcept {
+ return _Left.address() == _Right.address();
+}
+
+template <typename _PromiseT>
+bool operator!=(coroutine_handle<_PromiseT> const &_Left,
+ coroutine_handle<_PromiseT> const &_Right) noexcept {
+ return !(_Left == _Right);
+}
+
+struct noop_coroutine_promise {};
+
+template <>
+struct coroutine_handle<noop_coroutine_promise> {
+ operator coroutine_handle<>() const noexcept {
+ return coroutine_handle<>::from_address(address());
+ }
+
+ constexpr explicit operator bool() const noexcept { return true; }
+ constexpr bool done() const noexcept { return false; }
+
+ constexpr void operator()() const noexcept {}
+ constexpr void resume() const noexcept {}
+ constexpr void destroy() const noexcept {}
+
+ noop_coroutine_promise &promise() const noexcept {
+ return *static_cast<noop_coroutine_promise *>(
+ __builtin_coro_promise(this->__handle_, alignof(noop_coroutine_promise), false));
+ }
+
+ constexpr void *address() const noexcept { return __handle_; }
+
+private:
+ friend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept;
+
+ coroutine_handle() noexcept {
+ this->__handle_ = __builtin_coro_noop();
+ }
+
+ void *__handle_ = nullptr;
+};
+
+using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
+
+inline noop_coroutine_handle noop_coroutine() noexcept { return noop_coroutine_handle(); }
+
+struct suspend_always {
+ bool await_ready() noexcept { return false; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+struct suspend_never {
+ bool await_ready() noexcept { return true; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+
+} // namespace std
+
+//--- task.cppm
+module;
+#include "coroutine.h"
+export module task;
+export template <typename T>
+struct [[clang::coro_await_elidable]] Task {
+ struct promise_type {
+ struct FinalAwaiter {
+ bool await_ready() const noexcept { return false; }
+
+ template <typename P>
+ std::coroutine_handle<> await_suspend(std::coroutine_handle<P> coro) noexcept {
+ if (!coro)
+ return std::noop_coroutine();
+ return coro.promise().continuation;
+ }
+ void await_resume() noexcept {}
+ };
+
+ Task get_return_object() noexcept {
+ return std::coroutine_handle<promise_type>::from_promise(*this);
+ }
+
+ std::suspend_always initial_suspend() noexcept { return {}; }
+ FinalAwaiter final_suspend() noexcept { return {}; }
+ void unhandled_exception() noexcept {}
+ void return_value(T x) noexcept {
+ value = x;
+ }
+
+ std::coroutine_handle<> continuation;
+ T value;
+ };
+
+ Task(std::coroutine_handle<promise_type> handle) : handle(handle) {}
+ ~Task() {
+ if (handle)
+ handle.destroy();
+ }
+
+ struct Awaiter {
+ Awaiter(Task *t) : task(t) {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(std::coroutine_handle<void> continuation) noexcept {}
+ T await_resume() noexcept {
+ return task->handle.promise().value;
+ }
+
+ Task *task;
+ };
+
+ auto operator co_await() {
+ return Awaiter{this};
+ }
+
+private:
+ std::coroutine_handle<promise_type> handle;
+};
+
+inline Task<int> callee() {
+ co_return 1;
+}
+
+export inline Task<int> elidable() {
+ co_return co_await callee();
+}
+
+namespace std {
+ export using std::coroutine_traits;
+ export using std::coroutine_handle;
+ export using std::suspend_always;
+}
+
+//--- user.cpp
+// expected-no-diagnostics
+import task;
+Task<int> test() {
+ co_return co_await elidable();
+}
diff --git a/clang/test/Modules/crash-vfs-path-symlink-component.m b/clang/test/Modules/crash-vfs-path-symlink-component.m
index fe6e4d6ff424..45a68659a825 100644
--- a/clang/test/Modules/crash-vfs-path-symlink-component.m
+++ b/clang/test/Modules/crash-vfs-path-symlink-component.m
@@ -1,4 +1,10 @@
-// REQUIRES: crash-recovery, shell
+// Needs symlinks
+// UNSUPPORTED: system-windows
+// env -u is not supported on AIX.
+// TODO(boomanaiden154): Remove this once we have switched over to lit's
+// internal shell which does support env -u.
+// UNSUPPORTED: target={{.*}}-zos{{.*}}, target={{.*}}-aix{{.*}}
+// REQUIRES: crash-recovery
// FIXME: This XFAIL is cargo-culted from crash-report.c. Do we need it?
// XFAIL: target={{.*-windows-gnu}}
@@ -59,7 +65,7 @@
// %/t/i directory containing the symlink component.
// RUN: rm -rf %/t/i
-// RUN: unset FORCE_CLANG_DIAGNOSTICS_CRASH
+// RUN: env -u FORCE_CLANG_DIAGNOSTICS_CRASH \
// RUN: %clang -E %s -I %/t/i -isysroot %/t/sysroot/ \
// RUN: -ivfsoverlay %t/crash-vfs-*.cache/vfs/vfs.yaml -fmodules \
// RUN: -fmodules-cache-path=%t/m/ 2>&1 \
diff --git a/clang/test/Modules/crash-vfs-path-traversal.m b/clang/test/Modules/crash-vfs-path-traversal.m
index d6c9a0f2aeb8..8ab24755ed76 100644
--- a/clang/test/Modules/crash-vfs-path-traversal.m
+++ b/clang/test/Modules/crash-vfs-path-traversal.m
@@ -1,5 +1,11 @@
-// REQUIRES: crash-recovery, shell
+// REQUIRES: crash-recovery
// UNSUPPORTED: ms-sdk, target={{.*-(ps4|ps5)}}
+// Some assertions in this test use Linux style (/) file paths.
+// UNSUPPORTED: system-windows
+// env -u is not supported on AIX.
+// TODO(boomanaiden154): Remove this once we have switched over to lit's
+// internal shell which does support env -u.
+// UNSUPPORTED: target={{.*}}-zos{{.*}}, target={{.*}}-aix{{.*}}
// FIXME: Canonicalizing paths to remove relative traversal components
// currenty fails a unittest on windows and is disable by default.
@@ -57,7 +63,7 @@
// RUN: sed -e "s@usr/include@usr/include/../include@g" \
// RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml > %t/vfs.yaml
// RUN: cp %t/vfs.yaml %t/crash-vfs-*.cache/vfs/vfs.yaml
-// RUN: unset FORCE_CLANG_DIAGNOSTICS_CRASH
+// RUN: env -u FORCE_CLANG_DIAGNOSTICS_CRASH \
// RUN: %clang -E %s -I %S/Inputs/crash-recovery -isysroot %/t/i/ \
// RUN: -ivfsoverlay %t/crash-vfs-*.cache/vfs/vfs.yaml -fmodules \
// RUN: -fmodules-cache-path=%t/m/ 2>&1 \
diff --git a/clang/test/Modules/crash-vfs-relative-overlay.m b/clang/test/Modules/crash-vfs-relative-overlay.m
index 27f9c8d89066..048c65b90913 100644
--- a/clang/test/Modules/crash-vfs-relative-overlay.m
+++ b/clang/test/Modules/crash-vfs-relative-overlay.m
@@ -1,4 +1,9 @@
-// REQUIRES: crash-recovery, shell
+// UNSUPPORTED: system-windows
+// REQUIRES: crash-recovery
+// env -u is not supported on AIX.
+// TODO(boomanaiden154): Remove this once we have switched over to lit's
+// internal shell which does support env -u.
+// UNSUPPORTED: target={{.*}}-zos{{.*}}, target={{.*}}-aix{{.*}}
// FIXME: This XFAIL is cargo-culted from crash-report.c. Do we need it?
// XFAIL: target={{.*-windows-gnu}}
@@ -52,7 +57,7 @@
// Test that reading the YAML file will yield the correct path after
// the overlay dir is prefixed to access headers in .cache/vfs directory.
-// RUN: unset FORCE_CLANG_DIAGNOSTICS_CRASH
+// RUN: env -u FORCE_CLANG_DIAGNOSTICS_CRASH \
// RUN: %clang -E %s -I %S/Inputs/crash-recovery/usr/include -isysroot %/t/i/ \
// RUN: -ivfsoverlay %t/crash-vfs-*.cache/vfs/vfs.yaml -fmodules \
// RUN: -fmodules-cache-path=%t/m/ 2>&1 \
diff --git a/clang/test/Modules/implicit-opt-level.c b/clang/test/Modules/implicit-opt-level.c
new file mode 100644
index 000000000000..f6f1f58d31d7
--- /dev/null
+++ b/clang/test/Modules/implicit-opt-level.c
@@ -0,0 +1,15 @@
+// This test checks that under implicit modules, different optimization levels
+// get different context hashes.
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+//--- module.modulemap
+module M { header "M.h" }
+//--- M.h
+//--- tu.c
+#include "M.h"
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -O0 -fsyntax-only %t/tu.c
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -O1 -fsyntax-only %t/tu.c
+// RUN: find %t/cache -name "M-*.pcm" | count 2
diff --git a/clang/test/Modules/merge-records.cppm b/clang/test/Modules/merge-records.cppm
index dee41bda210b..bf06d7c7cb89 100644
--- a/clang/test/Modules/merge-records.cppm
+++ b/clang/test/Modules/merge-records.cppm
@@ -31,27 +31,6 @@ union U {
int c;
};
-//--- another_records.h
-struct A {
- int a;
- double b;
- float c;
-};
-
-struct NoNameEntity {
- struct {
- int a;
- unsigned b;
- long c;
- };
-};
-
-union U {
- int a;
- double b;
- short c;
-};
-
//--- A.cppm
module;
#include "records.h"
diff --git a/clang/test/Modules/modules-cache-path-canonicalization-output.c b/clang/test/Modules/modules-cache-path-canonicalization-output.c
new file mode 100644
index 000000000000..ad71b69e5299
--- /dev/null
+++ b/clang/test/Modules/modules-cache-path-canonicalization-output.c
@@ -0,0 +1,18 @@
+// This checks that implicitly-built modules produce identical PCM
+// files regardless of the spelling of the same module cache path.
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fsyntax-only %t/tu.c \
+// RUN: -fmodules-cache-path=%t/cache -fdisable-module-hash
+// RUN: mv %t/cache/M.pcm %t/M.pcm
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fsyntax-only %t/tu.c \
+// RUN: -fmodules-cache-path=%t/./cache -fdisable-module-hash
+// RUN: diff %t/./cache/M.pcm %t/M.pcm
+
+//--- tu.c
+#include "M.h"
+//--- M.h
+//--- module.modulemap
+module M { header "M.h" }
diff --git a/clang/test/Modules/safe_buffers_optout.cpp b/clang/test/Modules/safe_buffers_optout.cpp
index 8c3d6a235d39..39020a48925e 100644
--- a/clang/test/Modules/safe_buffers_optout.cpp
+++ b/clang/test/Modules/safe_buffers_optout.cpp
@@ -96,7 +96,7 @@ int textual(int *p) {
// `safe_buffers_test_base`. (So the module dependencies form a DAG.)
// No expected warnings from base.h, test_sub1, or test_sub2 because they are
-// in seperate modules, and the explicit commands that builds them have no
+// in separate modules, and the explicit commands that builds them have no
// `-Wunsafe-buffer-usage`.
int foo(int * p) {
@@ -122,7 +122,7 @@ int foo(int * p) {
// `safe_buffers_test_base`. (So the module dependencies form a DAG.)
// No expected warnings from base.h, test_sub1, or test_sub2 because they are
-// in seperate modules, and the explicit commands that builds them have no
+// in separate modules, and the explicit commands that builds them have no
// `-Wunsafe-buffer-usage`.
int foo(int * p) {
diff --git a/clang/test/Modules/umbrella_dir_order.m b/clang/test/Modules/umbrella_dir_order.m
new file mode 100644
index 000000000000..5faa1f940080
--- /dev/null
+++ b/clang/test/Modules/umbrella_dir_order.m
@@ -0,0 +1,11 @@
+// RUN: cd %S
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x objective-c -fmodule-name=x -emit-module Inputs/umbrella_header_order/module.modulemap -o %t/mod.pcm
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t/mod.pcm | FileCheck %s
+
+// CHECK: <INPUT_FILE abbrevid=4 op0=1 op1=36 op2=0 op3=0 op4=0 op5=1 op6=1 op7=16/> blob data = 'module.modulemap'
+// CHECK: <INPUT_FILE abbrevid=4 op0=2 op1=0 op2=0 op3=0 op4=0 op5=0 op6=0 op7=12/> blob data = 'umbrella{{[/\\]}}A.h'
+// CHECK: <INPUT_FILE abbrevid=4 op0=3 op1=0 op2=0 op3=0 op4=0 op5=0 op6=0 op7=12/> blob data = 'umbrella{{[/\\]}}B.h'
+// CHECK: <INPUT_FILE abbrevid=4 op0=4 op1=0 op2=0 op3=0 op4=0 op5=0 op6=0 op7=12/> blob data = 'umbrella{{[/\\]}}C.h'
+// CHECK: <INPUT_FILE abbrevid=4 op0=5 op1=0 op2=0 op3=0 op4=0 op5=0 op6=0 op7=12/> blob data = 'umbrella{{[/\\]}}D.h'
+// CHECK: <INPUT_FILE abbrevid=4 op0=6 op1=0 op2=0 op3=0 op4=0 op5=0 op6=0 op7=12/> blob data = 'umbrella{{[/\\]}}E.h'
+// CHECK: <INPUT_FILE abbrevid=4 op0=7 op1=0 op2=0 op3=0 op4=0 op5=0 op6=0 op7=12/> blob data = 'umbrella{{[/\\]}}F.h'