]> granicus.if.org Git - clang/commitdiff
[modules] When a tag type that was imported from a module is referenced via an
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 6 Jan 2016 03:52:10 +0000 (03:52 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 6 Jan 2016 03:52:10 +0000 (03:52 +0000)
elaborated-type-specifier, create a declaration of it to track that the current
module makes it visible too.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@256907 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaDecl.cpp
test/Modules/tag-injection.cpp [new file with mode: 0644]

index a41faa3880daa13e9c8e8e4ae4c5a1b2d6fd4da2..f27fb2b1071263b7ffaeca3d50618a65b4b82516 100644 (file)
@@ -12277,16 +12277,35 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
         if (!Invalid) {
           // If this is a use, just return the declaration we found, unless
           // we have attributes.
-
-          // FIXME: In the future, return a variant or some other clue
-          // for the consumer of this Decl to know it doesn't own it.
-          // For our current ASTs this shouldn't be a problem, but will
-          // need to be changed with DeclGroups.
-          if (!Attr &&
-              ((TUK == TUK_Reference &&
-                (!PrevTagDecl->getFriendObjectKind() || getLangOpts().MicrosoftExt))
-               || TUK == TUK_Friend))
-            return PrevTagDecl;
+          if (TUK == TUK_Reference || TUK == TUK_Friend) {
+            if (Attr) {
+              // FIXME: Diagnose these attributes. For now, we create a new
+              // declaration to hold them.
+            } else if (TUK == TUK_Reference &&
+                       (PrevTagDecl->getFriendObjectKind() ==
+                            Decl::FOK_Undeclared ||
+                        getOwningModule(PrevDecl) !=
+                            PP.getModuleContainingLocation(KWLoc)) &&
+                       SS.isEmpty()) {
+              // This declaration is a reference to an existing entity, but
+              // has different visibility from that entity: it either makes
+              // a friend visible or it makes a type visible in a new module.
+              // In either case, create a new declaration. We only do this if
+              // the declaration would have meant the same thing if no prior
+              // declaration were found, that is, if it was found in the same
+              // scope where we would have injected a declaration.
+              DeclContext *InjectedDC = CurContext;
+              while (!InjectedDC->isFileContext() &&
+                     !InjectedDC->isFunctionOrMethod())
+                InjectedDC = InjectedDC->getParent();
+              if (!InjectedDC->getRedeclContext()->Equals(
+                  PrevDecl->getDeclContext()->getRedeclContext()))
+                return PrevTagDecl;
+              // This is in the injected scope, create a new declaration.
+            } else {
+              return PrevTagDecl;
+            }
+          }
 
           // Diagnose attempts to redefine a tag.
           if (TUK == TUK_Definition) {
diff --git a/test/Modules/tag-injection.cpp b/test/Modules/tag-injection.cpp
new file mode 100644 (file)
index 0000000..75c8b5f
--- /dev/null
@@ -0,0 +1,22 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: touch %t/a.h
+// RUN: echo 'struct X {};' > %t/b.h
+// RUN: echo 'module X { module a { header "a.h" } module b { header "b.h" } }' > %t/x.modulemap
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -fmodule-map-file=%t/x.modulemap %s -I%t -verify -fmodules-local-submodule-visibility -std=c++11
+
+#include "a.h"
+
+struct A {
+  // This use of 'struct X' makes the declaration (but not definition) of X visible.
+  virtual void f(struct X *p);
+};
+
+namespace N {
+  struct B : A {
+    void f(struct X *q) override;
+  };
+}
+
+X x; // expected-error {{definition of 'X' must be imported from module 'X.b' before it is required}}
+// expected-note@b.h:1 {{here}}