summaryrefslogtreecommitdiffstats
path: root/test/CXX/basic
diff options
context:
space:
mode:
Diffstat (limited to 'test/CXX/basic')
-rw-r--r--test/CXX/basic/basic.link/p1.cpp57
-rw-r--r--test/CXX/basic/basic.link/p2.cpp16
-rw-r--r--test/CXX/basic/basic.link/p3.cpp53
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp344
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp56
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp16
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp64
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp93
-rw-r--r--test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp86
9 files changed, 785 insertions, 0 deletions
diff --git a/test/CXX/basic/basic.link/p1.cpp b/test/CXX/basic/basic.link/p1.cpp
new file mode 100644
index 0000000000..c6a119aa7f
--- /dev/null
+++ b/test/CXX/basic/basic.link/p1.cpp
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_PRIVATE_FRAG %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_PRIVATE_FRAG %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
+// RUN: %clang_cc1 -std=c++2a -verify -DEXPORT_FRAGS %s
+
+#ifndef NO_GLOBAL_FRAG
+#ifdef EXPORT_FRAGS
+export // expected-error {{global module fragment cannot be exported}}
+#endif
+module;
+#ifdef NO_MODULE_DECL
+// expected-error@-2 {{missing 'module' declaration at end of global module fragment introduced here}}
+#endif
+#endif
+
+extern int a; // #a1
+
+#ifndef NO_MODULE_DECL
+export module Foo;
+#ifdef NO_GLOBAL_FRAG
+// expected-error@-2 {{module declaration must occur at the start of the translation unit}}
+// expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}}
+#endif
+
+// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
+// expected-note@#a1 {{previous decl}}
+#endif
+
+int a; // #a2
+extern int b;
+
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+
+#ifndef NO_PRIVATE_FRAG
+#ifdef EXPORT_FRAGS
+export // expected-error {{private module fragment cannot be exported}}
+#endif
+module :private; // #priv-frag
+#ifdef NO_MODULE_DECL
+// expected-error@-2 {{private module fragment declaration with no preceding module declaration}}
+#endif
+#endif
+
+int b; // ok
+
+
+#ifndef NO_PRIVATE_FRAG
+#ifndef NO_MODULE_DECL
+module :private; // expected-error {{private module fragment redefined}}
+// expected-note@#priv-frag {{previous definition is here}}
+#endif
+#endif
diff --git a/test/CXX/basic/basic.link/p2.cpp b/test/CXX/basic/basic.link/p2.cpp
new file mode 100644
index 0000000000..54e347c91e
--- /dev/null
+++ b/test/CXX/basic/basic.link/p2.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -verify
+// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -emit-module-interface -o %t.pcm
+// RUN: %clang_cc1 -std=c++2a -UEXPORT %s -verify -fmodule-file=%t.pcm
+
+#ifdef EXPORT
+// expected-no-diagnostics
+export
+#else
+// expected-note@+2 {{add 'export' here}}
+#endif
+module M;
+
+#ifndef EXPORT
+// expected-error@+2 {{private module fragment in module implementation unit}}
+#endif
+module :private;
diff --git a/test/CXX/basic/basic.link/p3.cpp b/test/CXX/basic/basic.link/p3.cpp
new file mode 100644
index 0000000000..23f39d11b6
--- /dev/null
+++ b/test/CXX/basic/basic.link/p3.cpp
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=1
+// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=2
+
+module;
+
+#if IMPORT_ERROR != 2
+struct import { struct inner {}; };
+#endif
+struct module { struct inner {}; };
+
+constexpr int n = 123;
+
+export module m; // #1
+
+// Import errors are fatal, so we test them in isolation.
+#if IMPORT_ERROR == 1
+import x = {}; // expected-error {{module 'x' not found}}
+
+#elif IMPORT_ERROR == 2
+struct X;
+template<int> struct import;
+template<> struct import<n> {
+ static X y;
+};
+
+// This is not valid because the 'import <n>' is a pp-import, even though it
+// grammatically can't possibly be an import declaration.
+struct X {} import<n>::y; // expected-error {{'n' file not found}}
+
+#else
+module y = {}; // expected-error {{multiple module declarations}} expected-error 2{{}}
+// expected-note@#1 {{previous module declaration}}
+
+::import x = {};
+::module y = {};
+
+import::inner xi = {};
+module::inner yi = {};
+
+namespace N {
+ module a;
+ import b;
+}
+
+extern "C++" module cxxm;
+extern "C++" import cxxi;
+
+template<typename T> module module_var_template;
+
+// This is a variable named 'import' that shadows the type 'import' above.
+struct X {} import;
+#endif
diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp
new file mode 100644
index 0000000000..7562e64b17
--- /dev/null
+++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-associated-namespaces-classes.cpp
@@ -0,0 +1,344 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
+
+// Attempt to test each rule for forming associated namespaces
+// and classes as described in [basic.lookup.argdep]p2.
+
+// fundamental type: no associated namespace and no associated class
+namespace adl_fundamental_type {
+ constexpr int g(char) { return 1; } // #1
+ template <typename T> constexpr int foo(T t) { return g(t); }
+ constexpr int g(int) { return 2; } // #2 not found
+ void test() {
+ static_assert(foo(0) == 1); // ok, #1
+ }
+}
+
+// class type:
+// associated classes: itself, the class of which it is a member (if any),
+// direct and indirect base classes
+// associated namespaces: innermost enclosing namespaces of associated classes
+namespace adl_class_type {
+ // associated class: itself, simple case
+ namespace X1 {
+ namespace N {
+ struct S {};
+ void f(S); // found
+ }
+ void g(N::S); // not found
+ };
+ void test1() {
+ f(X1::N::S{}); // ok
+ g(X1::N::S{}); // expected-error {{use of undeclared identifier}}
+ }
+
+ // associated class: itself, local type
+ namespace X2 {
+ auto foo() {
+ struct S {} s;
+ return s;
+ }
+ using S = decltype(foo());
+ void f(S); // #1
+ }
+ void test2() {
+ f(X2::S{}); // This is well-formed; X2 is the innermost enclosing namespace
+ // of the local struct S. Calls #1.
+ }
+
+ // associated class: the parent class
+ namespace X3 {
+ struct S {
+ struct T {};
+ friend void f(T);
+ };
+ }
+ void test3() {
+ f(X3::S::T{}); // ok
+ }
+
+ // associated class: direct and indirect base classes
+ namespace X4 {
+ namespace IndirectBaseNamespace {
+ struct IndirectBase {};
+ void f(IndirectBase); // #1
+ }
+ namespace DirectBaseNamespace {
+ struct DirectBase : IndirectBaseNamespace::IndirectBase {};
+ void g(DirectBase); // #2
+ }
+ struct S : DirectBaseNamespace::DirectBase {};
+ }
+ void test4() {
+ f(X4::S{}); // ok, #1
+ g(X4::S{}); // ok, #2
+ }
+
+ // associated class: itself, lambda
+ namespace X5 {
+ namespace N {
+ auto get_lambda() { return [](){}; }
+ void f(decltype(get_lambda()));
+ }
+
+ void test5() {
+ auto lambda = N::get_lambda();
+ f(lambda); // ok
+ }
+ }
+
+ // The parameter types and return type of a lambda's operator() do not
+ // contribute to the associated namespaces and classes of the lambda itself.
+ namespace X6 {
+ namespace N {
+ struct A {};
+ template<class T> constexpr int f(T) { return 1; }
+ }
+
+ constexpr int f(N::A (*)()) { return 2; }
+ constexpr int f(void (*)(N::A)) { return 3; }
+
+ void test() {
+ constexpr auto lambda = []() -> N::A { return {}; };
+ static_assert(f(lambda) == 2);
+
+ constexpr auto lambda2 = [](N::A) {};
+ static_assert(f(lambda2) == 3);
+ }
+ }
+} // namespace adl_class_type
+
+// class template specialization: as for class type plus
+// for non-type template arguments:
+// - nothing
+// for type template arguments:
+// - associated namespaces and classes of the type template arguments
+// for template template arguments:
+// - namespaces of which template template arguments are member of
+// - classes of which member template used as template template arguments
+// are member of
+namespace adl_class_template_specialization_type {
+ // non-type template argument
+ namespace X1 {
+ namespace BaseNamespace { struct Base {}; }
+ namespace N { struct S : BaseNamespace::Base {}; }
+ template <N::S *> struct C {};
+ namespace N {
+ template <S *p> void X1_f(C<p>); // #1
+ }
+ namespace BaseNamespace {
+ template <N::S *p> void X1_g(C<p>); // #2
+ }
+ template <N::S *p> void X1_h(C<p>); // #3
+ }
+ void test1() {
+ constexpr X1::N::S *p = nullptr;
+ X1::C<p> c;
+ X1_f(c); // N is not added to the set of associated namespaces
+ // and #1 is not found...
+ // expected-error@-2 {{use of undeclared identifier}}
+ X1_g(c); // ... nor is #2 ...
+ // expected-error@-1 {{use of undeclared identifier}}
+ X1_h(c); // ... but the namespace X1 is added and #3 is found.
+ }
+
+ // type template argument
+ namespace X2 {
+ template <typename T> struct C {};
+ namespace BaseNamespace { struct Base {}; }
+ namespace N { struct S : BaseNamespace::Base {}; }
+ namespace N {
+ template <typename T> void X2_f(C<T>); // #1
+ }
+ namespace BaseNamespace {
+ template <typename T> void X2_g(C<T>); // #2
+ }
+ template <typename T> void X2_h(C<T>); // #2
+ }
+ void test2() {
+ X2::C<X2::N::S> c;
+ X2_f(c); // N is added to the set of associated namespaces and #1 is found.
+ X2_g(c); // Similarly BaseNamespace is added and #2 is found.
+ X2_h(c); // As before, X2 is also added and #3 is found.
+ }
+
+ // template template argument
+ namespace X3 {
+ template <template <typename> class TT> struct C {};
+ namespace N {
+ template <typename T> struct Z {};
+ void X3_f(C<Z>); // #1
+ }
+ struct M {
+ template <typename T> struct Z {};
+ friend void X3_g(C<Z>); // #2
+ };
+ }
+ void test3() {
+ X3::C<X3::N::Z> c1;
+ X3::C<X3::M::Z> c2;
+ X3_f(c1); // ok, namespace N is added, #1
+ X3_g(c2); // ok, struct M is added, #2
+ }
+}
+
+// enumeration type:
+// associated namespace: innermost enclosing namespace of its declaration.
+// associated class: if the enumeration is a class member, the member's class.
+namespace adl_enumeration_type {
+ namespace N {
+ enum E : int;
+ void f(E);
+ struct S {
+ enum F : int;
+ friend void g(F);
+ };
+ auto foo() {
+ enum G {} g;
+ return g;
+ }
+ using G = decltype(foo());
+ void h(G);
+ }
+
+ void test() {
+ N::E e;
+ f(e); // ok
+ N::S::F f;
+ g(f); // ok
+ N::G g;
+ h(g); // ok
+
+ }
+}
+
+// pointer and reference type:
+// associated namespaces and classes of the pointee type
+// array type:
+// associated namespaces and classes of the base type
+namespace adl_point_array_reference_type {
+ namespace N {
+ struct S {};
+ void f(S *);
+ void f(S &);
+ }
+
+ void test() {
+ N::S *p;
+ f(p); // ok
+ extern N::S &r;
+ f(r); // ok
+ N::S a[2];
+ f(a); // ok
+ }
+}
+
+// function type:
+// associated namespaces and classes of the function parameter types
+// and the return type.
+namespace adl_function_type {
+ namespace M { struct T; }
+ namespace N {
+ struct S {};
+ void f(S (*)(M::T));
+ };
+ namespace M {
+ struct T {};
+ void g(N::S (*)(T));
+ }
+
+ void test() {
+ extern N::S x(M::T);
+ f(x); // ok
+ g(x); // ok
+ }
+}
+
+// pointer to member function:
+// associated namespaces and classes of the class, parameter types
+// and return type.
+namespace adl_pointer_to_member_function {
+ namespace M { struct C; }
+ namespace L { struct T; }
+ namespace N {
+ struct S {};
+ void f(N::S (M::C::*)(L::T));
+ }
+ namespace L {
+ struct T {};
+ void g(N::S (M::C::*)(L::T));
+ }
+ namespace M {
+ struct C {};
+ void h(N::S (M::C::*)(L::T));
+ }
+
+ void test() {
+ N::S (M::C::*p)(L::T);
+ f(p); // ok
+ g(p); // ok
+ h(p); // ok
+ }
+}
+
+// pointer to member:
+// associated namespaces and classes of the class and of the member type.
+namespace adl_pointer_to_member {
+ namespace M { struct C; }
+ namespace N {
+ struct S {};
+ void f(N::S (M::C::*));
+ }
+ namespace M {
+ struct C {};
+ void g(N::S (M::C::*));
+ }
+
+ void test() {
+ N::S (M::C::*p);
+ f(p); // ok
+ g(p); // ok
+ }
+}
+
+// [...] if the argument is the name or address of a set of overloaded
+// functions and/or function templates, its associated classes and namespaces
+// are the union of those associated with each of the members of the set,
+// i.e., the classes and namespaces associated with its parameter types and
+// return type.
+//
+// Additionally, if the aforementioned set of overloaded functions is named
+// with a template-id, its associated classes and namespaces also include
+// those of its type template-arguments and its template template-arguments.
+//
+// CWG 33 for the union rule. CWG 997 for the template-id rule.
+namespace adl_overload_set {
+ namespace N {
+ struct S {};
+ constexpr int f(int (*g)()) { return g(); }
+ // expected-note@-1 2{{'N::f' declared here}}
+ template <typename T> struct Q;
+ }
+
+ constexpr int g1() { return 1; }
+ constexpr int g1(N::S) { return 2; }
+
+ template <typename T> constexpr int g2() { return 3; }
+
+ // Inspired from CWG 997.
+ constexpr int g3() { return 4; }
+ template <typename T> constexpr int g3(T, N::Q<T>) { return 5; }
+
+ void test() {
+ static_assert(f(g1) == 1, ""); // Well-formed from the union rule above
+ static_assert(f(g2<N::S>) == 3, ""); // FIXME: Well-formed from the template-id rule above.
+ // expected-error@-1 {{use of undeclared}}
+
+ // A objection was raised during review against implementing the
+ // template-id rule. Currently only GCC implements it. Implementing
+ // it would weaken the argument to remove it in the future since
+ // actual real code might start to depend on it.
+
+ static_assert(f(g3) == 4, ""); // FIXME: Also well-formed from the union rule.
+ // expected-error@-1 {{use of undeclared}}
+ }
+}
diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp
new file mode 100644
index 0000000000..19054f6415
--- /dev/null
+++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2-inline-namespace.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+// C++11 [basic.lookup.argdep]p2
+//
+// [...] If an associated namespace is an inline namespace (10.3.1), its
+// enclosing namespace is also included in the set. If an associated
+// namespace directly contains inline namespaces, those inline namespaces
+// are also included in the set.
+
+namespace test1 {
+ namespace L {
+ namespace M {
+ inline namespace N {
+ inline namespace O {
+ struct S {};
+ void f1(S);
+ }
+ void f2(S);
+ }
+ void f3(S);
+ }
+ void f4(M::S); // expected-note {{declared here}}
+ }
+
+ void test() {
+ L::M::S s;
+ f1(s); // ok
+ f2(s); // ok
+ f3(s); // ok
+ f4(s); // expected-error {{use of undeclared}}
+ }
+}
+
+namespace test2 {
+ namespace L {
+ struct S {};
+ inline namespace M {
+ inline namespace N {
+ inline namespace O {
+ void f1(S);
+ }
+ void f2(S);
+ }
+ void f3(S);
+ }
+ void f4(S);
+ }
+
+ void test() {
+ L::S s;
+ f1(s); // ok
+ f2(s); // ok
+ f3(s); // ok
+ f4(s); // ok
+ }
+}
diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
index e352bbe83c..b19c81f2f3 100644
--- a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
+++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p2.cpp
@@ -132,3 +132,19 @@ namespace test8 {
test8_function(ref);
}
}
+
+
+
+// [...] Typedef names and using-declarations used to specify the types
+// do not contribute to this set.
+namespace typedef_names_and_using_declarations {
+ namespace N { struct S {}; void f(S); }
+ namespace M { typedef N::S S; void g1(S); } // expected-note {{declared here}}
+ namespace L { using N::S; void g2(S); } // expected-note {{declared here}}
+ void test() {
+ M::S s;
+ f(s); // ok
+ g1(s); // expected-error {{use of undeclared}}
+ g2(s); // expected-error {{use of undeclared}}
+ }
+}
diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp
index c4c2c8d605..88e06fc9ae 100644
--- a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp
+++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p3.cpp
@@ -18,3 +18,67 @@ namespace test0 {
}
};
}
+
+// If X contains [...] then Y is empty.
+// - a declaration of a class member
+namespace test_adl_suppression_by_class_member {
+ namespace N {
+ struct T {};
+ void f(T); // expected-note {{declared here}}
+ }
+ struct S {
+ void f();
+ void test() {
+ N::T t;
+ f(t); // expected-error {{too many arguments}}
+ }
+ };
+}
+
+// - a block-scope function declaration that is not a using-declaration
+namespace test_adl_suppression_by_block_scope {
+ namespace N {
+ struct S {};
+ void f(S);
+ }
+ namespace M { void f(int); } // expected-note 2{{candidate}}
+ void test1() {
+ N::S s;
+ using M::f;
+ f(s); // ok
+ }
+
+ void test2() {
+ N::S s;
+ extern void f(char); // expected-note {{passing argument to parameter here}}
+ f(s); // expected-error {{no viable conversion from 'N::S' to 'char'}}
+ }
+
+ void test3() {
+ N::S s;
+ extern void f(char); // expected-note {{candidate}}
+ using M::f;
+ f(s); // expected-error {{no matching function}}
+ }
+
+ void test4() {
+ N::S s;
+ using M::f;
+ extern void f(char); // expected-note {{candidate}}
+ f(s); // expected-error {{no matching function}}
+ }
+
+}
+
+// - a declaration that is neither a function nor a function template
+namespace test_adl_suppression_by_non_function {
+ namespace N {
+ struct S {};
+ void f(S);
+ }
+ void test() {
+ extern void (*f)();
+ N::S s;
+ f(s); // expected-error {{too many arguments}}
+ }
+}
diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp
index 2292fc540c..910eb0c1a8 100644
--- a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp
+++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp
@@ -67,3 +67,96 @@ namespace test1 {
foo(a, 10); // expected-error {{no matching function for call to 'foo'}}
}
}
+
+
+// Check the rules described in p4:
+// When considering an associated namespace, the lookup is the same as the lookup
+// performed when the associated namespace is used as a qualifier (6.4.3.2) except that:
+
+// - Any using-directives in the associated namespace are ignored.
+namespace test_using_directives {
+ namespace M { struct S; }
+ namespace N {
+ void f(M::S); // expected-note {{declared here}}
+ }
+ namespace M {
+ using namespace N;
+ struct S {};
+ }
+ void test() {
+ M::S s;
+ f(s); // expected-error {{use of undeclared}}
+ M::f(s); // ok
+ }
+}
+
+// - Any namespace-scope friend functions or friend function templates declared in
+// associated classes are visible within their respective namespaces even if
+// they are not visible during an ordinary lookup
+// (Note: For the friend declaration to be visible, the corresponding class must be
+// included in the set of associated classes. Merely including the namespace in
+// the set of associated namespaces is not enough.)
+namespace test_friend1 {
+ namespace N {
+ struct S;
+ struct T {
+ friend void f(S); // #1
+ };
+ struct S { S(); S(T); };
+ }
+
+ void test() {
+ N::S s;
+ N::T t;
+ f(s); // expected-error {{use of undeclared}}
+ f(t); // ok, #1
+ }
+}
+
+// credit: Arthur O’Dwyer
+namespace test_friend2 {
+ struct A {
+ struct B {
+ struct C {};
+ };
+ friend void foo(...); // #1
+ };
+
+ struct D {
+ friend void foo(...); // #2
+ };
+ template<class> struct E {
+ struct F {};
+ };
+
+ template<class> struct G {};
+ template<class> struct H {};
+ template<class> struct I {};
+ struct J { friend void foo(...) {} }; // #3
+
+ void test() {
+ A::B::C c;
+ foo(c); // #1 is not visible since A is not an associated class
+ // expected-error@-1 {{use of undeclared}}
+ E<D>::F f;
+ foo(f); // #2 is not visible since D is not an associated class
+ // expected-error@-1 {{use of undeclared}}
+ G<H<I<J> > > j;
+ foo(j); // ok, #3.
+ }
+}
+
+// - All names except those of (possibly overloaded) functions and
+// function templates are ignored.
+namespace test_other_names {
+ namespace N {
+ struct S {};
+ struct Callable { void operator()(S); };
+ static struct Callable Callable;
+ }
+
+ void test() {
+ N::S s;
+ Callable(s); // expected-error {{use of undeclared}}
+ }
+}
diff --git a/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp b/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
new file mode 100644
index 0000000000..55c5ce6c4c
--- /dev/null
+++ b/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
@@ -0,0 +1,86 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: echo '#ifndef FOO_H' > %t/foo.h
+// RUN: echo '#define FOO_H' >> %t/foo.h
+// RUN: echo 'extern int in_header;' >> %t/foo.h
+// RUN: echo '#endif' >> %t/foo.h
+// RUN: %clang_cc1 -std=c++2a -I%t -emit-module-interface -DINTERFACE %s -o %t.pcm
+// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=%t.pcm -DIMPLEMENTATION %s -verify -fno-modules-error-recovery
+// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=%t.pcm %s -verify -fno-modules-error-recovery
+
+#ifdef INTERFACE
+module;
+#include "foo.h"
+int global_module_fragment;
+export module A;
+export int exported;
+int not_exported;
+static int internal;
+
+module :private;
+int not_exported_private;
+static int internal_private;
+#else
+
+#ifdef IMPLEMENTATION
+module;
+#endif
+
+void test_early() {
+ in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
+ // expected-note@*{{previous}}
+
+ global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}}
+
+ exported = 1; // expected-error {{must be imported from module 'A'}}
+ // expected-note@p2.cpp:16 {{previous}}
+
+ not_exported = 1; // expected-error {{undeclared identifier}}
+
+ internal = 1; // expected-error {{undeclared identifier}}
+
+ not_exported_private = 1; // expected-error {{undeclared identifier}}
+
+ internal_private = 1; // expected-error {{undeclared identifier}}
+}
+
+#ifdef IMPLEMENTATION
+module A;
+#else
+import A;
+#endif
+
+void test_late() {
+ in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
+ // expected-note@*{{previous}}
+
+ global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}}
+
+ exported = 1;
+
+ not_exported = 1;
+#ifndef IMPLEMENTATION
+ // expected-error@-2 {{undeclared identifier 'not_exported'; did you mean 'exported'}}
+ // expected-note@p2.cpp:16 {{declared here}}
+#endif
+
+ internal = 1;
+#ifndef IMPLEMENTATION
+ // FIXME: should not be visible here
+ // expected-error@-3 {{undeclared identifier}}
+#endif
+
+ not_exported_private = 1;
+#ifndef IMPLEMENTATION
+ // FIXME: should not be visible here
+ // expected-error@-3 {{undeclared identifier}}
+#endif
+
+ internal_private = 1;
+#ifndef IMPLEMENTATION
+ // FIXME: should not be visible here
+ // expected-error@-3 {{undeclared identifier}}
+#endif
+}
+
+#endif