Add support for -fvisibility-ms-compat.

We treat this as an alternative to -fvisibility=<?>
which changes the default value visibility to "hidden"
and the default type visibility to "default".

Expose a -cc1 option for changing the default type
visibility, repurposing -fvisibility as the default
value visibility option (also setting type visibility
from it in the absence of a specific option).

rdar://13079314

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@175480 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def
index 60a7e83..a7c753e 100644
--- a/include/clang/Basic/LangOptions.def
+++ b/include/clang/Basic/LangOptions.def
@@ -147,8 +147,10 @@
 LANGOPT(BlocksRuntimeOptional , 1, 0, "optional blocks runtime")
 
 ENUM_LANGOPT(GC, GCMode, 2, NonGC, "Objective-C Garbage Collection mode")
-ENUM_LANGOPT(VisibilityMode, Visibility, 3, DefaultVisibility, 
-             "symbol visibility")
+ENUM_LANGOPT(ValueVisibilityMode, Visibility, 3, DefaultVisibility, 
+             "value symbol visibility")
+ENUM_LANGOPT(TypeVisibilityMode, Visibility, 3, DefaultVisibility, 
+             "type symbol visibility")
 ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff, 
              "stack protector mode")
 ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined,
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index b84f96b..3c5e75f 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -415,7 +415,9 @@
 def stack_protector_buffer_size : Separate<["-"], "stack-protector-buffer-size">,
   HelpText<"Lower bound for a buffer to be considered for stack protection">;
 def fvisibility : Separate<["-"], "fvisibility">,
-  HelpText<"Default symbol visibility">;
+  HelpText<"Default type and symbol visibility">;
+def ftype_visibility : Separate<["-"], "ftype-visibility">,
+  HelpText<"Default type visibility">;
 def ftemplate_depth : Separate<["-"], "ftemplate-depth">,
   HelpText<"Maximum depth of recursive template instantiation">;
 def fconstexpr_depth : Separate<["-"], "fconstexpr-depth">,
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 5fd2fff..cafd7d7 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -723,10 +723,14 @@
 def fuse_init_array : Flag<["-"], "fuse-init-array">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Use .init_array instead of .ctors">;
 def fverbose_asm : Flag<["-"], "fverbose-asm">, Group<f_Group>;
-def fvisibility_EQ : Joined<["-"], "fvisibility=">, Group<f_Group>;
+def fvisibility_EQ : Joined<["-"], "fvisibility=">, Group<f_Group>,
+  HelpText<"Set the default symbol visibility for all global declarations">;
 def fvisibility_inlines_hidden : Flag<["-"], "fvisibility-inlines-hidden">, Group<f_Group>,
   HelpText<"Give inline C++ member functions default visibility by default">,
   Flags<[CC1Option]>;
+def fvisibility_ms_compat : Flag<["-"], "fvisibility-ms-compat">, Group<f_Group>,
+  HelpText<"Give global types 'default' visibility and global functions and "
+           "variables 'hidden' visibility by default">;
 def fwrapv : Flag<["-"], "fwrapv">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Treat signed integer overflow as two's complement">;
 def fwritable_strings : Flag<["-"], "fwritable-strings">, Group<f_Group>, Flags<[CC1Option]>,
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 0183c0b..6e93dd6 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -80,7 +80,9 @@
 /// Is the given declaration a "type" or a "value" for the purposes of
 /// visibility computation?
 static bool usesTypeVisibility(const NamedDecl *D) {
-  return isa<TypeDecl>(D) || isa<ClassTemplateDecl>(D);
+  return isa<TypeDecl>(D) ||
+         isa<ClassTemplateDecl>(D) ||
+         isa<ObjCInterfaceDecl>(D);
 }
 
 /// Return the explicit visibility of the given declaration.
@@ -440,10 +442,15 @@
 
     // Add in global settings if the above didn't give us direct visibility.
     if (!LV.visibilityExplicit()) {
-      // FIXME: type visibility
-      LV.mergeVisibility(Context.getLangOpts().getVisibilityMode(),
-                         /*explicit*/ false);
-
+      // Use global type/value visibility as appropriate.
+      Visibility globalVisibility;
+      if (computation == LVForValue) {
+        globalVisibility = Context.getLangOpts().getValueVisibilityMode();
+      } else {
+        assert(computation == LVForType);
+        globalVisibility = Context.getLangOpts().getTypeVisibilityMode();
+      }
+      LV.mergeVisibility(globalVisibility, /*explicit*/ false);
 
       // If we're paying attention to global visibility, apply
       // -finline-visibility-hidden if this is an inline method.
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index f5ea38b..5b3b654 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -2523,9 +2523,19 @@
     CmdArgs.push_back(Args.MakeArgString(Twine(N)));
   }
 
-  if (const Arg *A = Args.getLastArg(options::OPT_fvisibility_EQ)) {
-    CmdArgs.push_back("-fvisibility");
-    CmdArgs.push_back(A->getValue());
+  // -fvisibility= and -fvisibility-ms-compat are of a piece.
+  if (const Arg *A = Args.getLastArg(options::OPT_fvisibility_EQ,
+                                     options::OPT_fvisibility_ms_compat)) {
+    if (A->getOption().matches(options::OPT_fvisibility_EQ)) {
+      CmdArgs.push_back("-fvisibility");
+      CmdArgs.push_back(A->getValue());
+    } else {
+      assert(A->getOption().matches(options::OPT_fvisibility_ms_compat));
+      CmdArgs.push_back("-fvisibility");
+      CmdArgs.push_back("hidden");
+      CmdArgs.push_back("-ftype-visibility");
+      CmdArgs.push_back("default");
+    }
   }
 
   Args.AddLastArg(CmdArgs, options::OPT_fvisibility_inlines_hidden);
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index f4bf4b7..05e0794 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1001,6 +1001,24 @@
   Opts.DollarIdents = !Opts.AsmPreprocessor;
 }
 
+/// Attempt to parse a visibility value out of the given argument.
+static Visibility parseVisibility(Arg *arg, ArgList &args,
+                                  DiagnosticsEngine &diags) {
+  StringRef value = arg->getValue();
+  if (value == "default") {
+    return DefaultVisibility;
+  } else if (value == "hidden") {
+    return HiddenVisibility;
+  } else if (value == "protected") {
+    // FIXME: diagnose if target does not support protected visibility
+    return ProtectedVisibility;
+  }
+
+  diags.Report(diag::err_drv_invalid_value)
+    << arg->getAsString(args) << value;
+  return DefaultVisibility;
+}
+
 static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
                           DiagnosticsEngine &Diags) {
   // FIXME: Cleanup per-file based stuff.
@@ -1132,17 +1150,19 @@
   if (Args.hasArg(OPT_fdelayed_template_parsing))
     Opts.DelayedTemplateParsing = 1;
 
-  StringRef Vis = Args.getLastArgValue(OPT_fvisibility, "default");
-  if (Vis == "default")
-    Opts.setVisibilityMode(DefaultVisibility);
-  else if (Vis == "hidden")
-    Opts.setVisibilityMode(HiddenVisibility);
-  else if (Vis == "protected")
-    // FIXME: diagnose if target does not support protected visibility
-    Opts.setVisibilityMode(ProtectedVisibility);
-  else
-    Diags.Report(diag::err_drv_invalid_value)
-      << Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis;
+  // The value-visibility mode defaults to "default".
+  if (Arg *visOpt = Args.getLastArg(OPT_fvisibility)) {
+    Opts.setValueVisibilityMode(parseVisibility(visOpt, Args, Diags));
+  } else {
+    Opts.setValueVisibilityMode(DefaultVisibility);
+  }
+
+  // The type-visibility mode defaults to the value-visibility mode.
+  if (Arg *typeVisOpt = Args.getLastArg(OPT_ftype_visibility)) {
+    Opts.setTypeVisibilityMode(parseVisibility(typeVisOpt, Args, Diags));
+  } else {
+    Opts.setTypeVisibilityMode(Opts.getValueVisibilityMode());
+  }
 
   if (Args.hasArg(OPT_fvisibility_inlines_hidden))
     Opts.InlineVisibilityHidden = 1;
diff --git a/test/CodeGenCXX/visibility-ms-compat.cpp b/test/CodeGenCXX/visibility-ms-compat.cpp
new file mode 100644
index 0000000..58a8fed
--- /dev/null
+++ b/test/CodeGenCXX/visibility-ms-compat.cpp
@@ -0,0 +1,112 @@
+// RUN: %clang_cc1 %s -std=c++11 -triple=x86_64-apple-darwin10 -fvisibility hidden -ftype-visibility default -emit-llvm -o %t
+// RUN: FileCheck %s < %t
+// RUN: FileCheck -check-prefix=CHECK-GLOBAL %s < %t
+
+// The two visibility options above are how we translate
+// -fvisibility-ms-compat in the driver.
+
+// rdar://13079314
+
+#define HIDDEN __attribute__((visibility("hidden")))
+#define PROTECTED __attribute__((visibility("protected")))
+#define DEFAULT __attribute__((visibility("default")))
+
+namespace std {
+  class type_info;
+};
+
+namespace test0 {
+  struct A {
+    static void foo();
+    static void bar();
+  };
+
+  void A::foo() { bar(); }
+  // CHECK: define hidden void @_ZN5test01A3fooEv()
+  // CHECK: declare void @_ZN5test01A3barEv()
+
+  const std::type_info &ti = typeid(A);
+  // CHECK-GLOBAL: @_ZTSN5test01AE = linkonce_odr constant
+  // CHECK-GLOBAL: @_ZTIN5test01AE = linkonce_odr unnamed_addr constant
+  // CHECK-GLOBAL: @_ZN5test02tiE = hidden constant
+}
+
+namespace test1 {
+  struct HIDDEN A {
+    static void foo();
+    static void bar();
+  };
+
+  void A::foo() { bar(); }
+  // CHECK: define hidden void @_ZN5test11A3fooEv()
+  // CHECK: declare hidden void @_ZN5test11A3barEv()
+
+  const std::type_info &ti = typeid(A);
+  // CHECK-GLOBAL: @_ZTSN5test11AE = linkonce_odr hidden constant
+  // CHECK-GLOBAL: @_ZTIN5test11AE = linkonce_odr hidden unnamed_addr constant
+  // CHECK-GLOBAL: @_ZN5test12tiE = hidden constant
+}
+
+namespace test2 {
+  struct DEFAULT A {
+    static void foo();
+    static void bar();
+  };
+
+  void A::foo() { bar(); }
+  // CHECK: define void @_ZN5test21A3fooEv()
+  // CHECK: declare void @_ZN5test21A3barEv()
+
+  const std::type_info &ti = typeid(A);
+  // CHECK-GLOBAL: @_ZTSN5test21AE = linkonce_odr constant
+  // CHECK-GLOBAL: @_ZTIN5test21AE = linkonce_odr unnamed_addr constant
+  // CHECK-GLOBAL: @_ZN5test22tiE = hidden constant
+}
+
+namespace test3 {
+  struct A { int x; };
+  template <class T> struct B {
+    static void foo() { bar(); }
+    static void bar();
+  };
+
+  template void B<A>::foo();
+  // CHECK: define weak_odr hidden void @_ZN5test31BINS_1AEE3fooEv()
+  // CHECK: declare void @_ZN5test31BINS_1AEE3barEv()
+
+  const std::type_info &ti = typeid(B<A>);
+  // CHECK-GLOBAL: @_ZTSN5test31BINS_1AEEE = linkonce_odr constant
+  // CHECK-GLOBAL: @_ZTIN5test31BINS_1AEEE = linkonce_odr unnamed_addr constant
+}
+
+namespace test4 {
+  struct A { int x; };
+  template <class T> struct DEFAULT B {
+    static void foo() { bar(); }
+    static void bar();
+  };
+
+  template void B<A>::foo();
+  // CHECK: define weak_odr void @_ZN5test41BINS_1AEE3fooEv()
+  // CHECK: declare void @_ZN5test41BINS_1AEE3barEv()
+
+  const std::type_info &ti = typeid(B<A>);
+  // CHECK-GLOBAL: @_ZTSN5test41BINS_1AEEE = linkonce_odr constant
+  // CHECK-GLOBAL: @_ZTIN5test41BINS_1AEEE = linkonce_odr unnamed_addr constant
+}
+
+namespace test5 {
+  struct A { int x; };
+  template <class T> struct HIDDEN B {
+    static void foo() { bar(); }
+    static void bar();
+  };
+
+  template void B<A>::foo();
+  // CHECK: define weak_odr hidden void @_ZN5test51BINS_1AEE3fooEv()
+  // CHECK: declare hidden void @_ZN5test51BINS_1AEE3barEv()
+
+  const std::type_info &ti = typeid(B<A>);
+  // CHECK-GLOBAL: @_ZTSN5test51BINS_1AEEE = linkonce_odr hidden constant
+  // CHECK-GLOBAL: @_ZTIN5test51BINS_1AEEE = linkonce_odr hidden unnamed_addr constant
+}
diff --git a/test/Driver/visibility.cpp b/test/Driver/visibility.cpp
new file mode 100644
index 0000000..cdbef97
--- /dev/null
+++ b/test/Driver/visibility.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang -### -S -fvisibility=hidden -fvisibility=default %s 2> %t.log
+// RUN: FileCheck -check-prefix=CHECK-1 %s < %t.log
+// CHECK-NOT: "-ftype-visibility"
+// CHECK-1: "-fvisibility" "default"
+// CHECK-NOT: "-ftype-visibility"
+
+// RUN: %clang -### -S -fvisibility=default -fvisibility=hidden %s 2> %t.log
+// RUN: FileCheck -check-prefix=CHECK-2 %s < %t.log
+// CHECK-NOT: "-ftype-visibility"
+// CHECK-2: "-fvisibility" "hidden"
+// CHECK-NOT: "-ftype-visibility"
+
+// RUN: %clang -### -S -fvisibility-ms-compat -fvisibility=hidden %s 2> %t.log
+// RUN: FileCheck -check-prefix=CHECK-3 %s < %t.log
+// CHECK-NOT: "-ftype-visibility"
+// CHECK-3: "-fvisibility" "hidden"
+// CHECK-NOT: "-ftype-visibility"
+
+// RUN: %clang -### -S -fvisibility-ms-compat -fvisibility=default %s 2> %t.log
+// RUN: FileCheck -check-prefix=CHECK-4 %s < %t.log
+// CHECK-NOT: "-ftype-visibility"
+// CHECK-4: "-fvisibility" "default"
+// CHECK-NOT: "-ftype-visibility"
+
+// RUN: %clang -### -S -fvisibility=hidden -fvisibility-ms-compat %s 2> %t.log
+// RUN: FileCheck -check-prefix=CHECK-5 %s < %t.log
+// CHECK-5: "-fvisibility" "hidden"
+// CHECK-5: "-ftype-visibility" "default"
+
+// RUN: %clang -### -S -fvisibility=default -fvisibility-ms-compat %s 2> %t.log
+// RUN: FileCheck -check-prefix=CHECK-6 %s < %t.log
+// CHECK-6: "-fvisibility" "hidden"
+// CHECK-6: "-ftype-visibility" "default"
+