[clang] Ignore template parameter visibility (PR #72092)

via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 13 01:04:55 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Fangrui Song (MaskRay)

<details>
<summary>Changes</summary>

When computing the visibility of a template
specialization/instantiation, GCC considers template arguments and
ignores type template parameters.

We currently consider non-type template parameters, which adds a lot of
complexity and is also unnecessary for the majority of cases.

Non-type template arguments that affect the visibility are mostly
`TemplateArgument::Declaration`. If a non-type template argument
involves a `NamedDecl` without a `VisibilityAttr `, the `case
TemplateArgument::Declaration` code in `getLVForTemplateArgumentList`
handles the desired cases.

```
struct HIDDEN foo {};
DEFAULT foo da; // has a VisibilityAttr and is used in a non-type template argument (uncommon)
foo za; // the majority of cases, no VisibilityAttr

template void zed<&da>(); // w/o the patch: hidden, w/ the patch: default
template void zed<&za>(); // no behavior change
```

For the updated tests, they all behave the same as GCC.


---
Full diff: https://github.com/llvm/llvm-project/pull/72092.diff


3 Files Affected:

- (modified) clang/lib/AST/Decl.cpp (+9-99) 
- (modified) clang/lib/AST/Linkage.h (-3) 
- (modified) clang/test/CodeGenCXX/visibility.cpp (+19-35) 


``````````diff
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index c5c2edf1bfe3aba..8a84dad4e4452ed 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -244,62 +244,6 @@ LinkageInfo LinkageComputer::getLVForType(const Type &T,
   return getTypeLinkageAndVisibility(&T);
 }
 
-/// Get the most restrictive linkage for the types in the given
-/// template parameter list.  For visibility purposes, template
-/// parameters are part of the signature of a template.
-LinkageInfo LinkageComputer::getLVForTemplateParameterList(
-    const TemplateParameterList *Params, LVComputationKind computation) {
-  LinkageInfo LV;
-  for (const NamedDecl *P : *Params) {
-    // Template type parameters are the most common and never
-    // contribute to visibility, pack or not.
-    if (isa<TemplateTypeParmDecl>(P))
-      continue;
-
-    // Non-type template parameters can be restricted by the value type, e.g.
-    //   template <enum X> class A { ... };
-    // We have to be careful here, though, because we can be dealing with
-    // dependent types.
-    if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
-      // Handle the non-pack case first.
-      if (!NTTP->isExpandedParameterPack()) {
-        if (!NTTP->getType()->isDependentType()) {
-          LV.merge(getLVForType(*NTTP->getType(), computation));
-        }
-        continue;
-      }
-
-      // Look at all the types in an expanded pack.
-      for (unsigned i = 0, n = NTTP->getNumExpansionTypes(); i != n; ++i) {
-        QualType type = NTTP->getExpansionType(i);
-        if (!type->isDependentType())
-          LV.merge(getTypeLinkageAndVisibility(type));
-      }
-      continue;
-    }
-
-    // Template template parameters can be restricted by their
-    // template parameters, recursively.
-    const auto *TTP = cast<TemplateTemplateParmDecl>(P);
-
-    // Handle the non-pack case first.
-    if (!TTP->isExpandedParameterPack()) {
-      LV.merge(getLVForTemplateParameterList(TTP->getTemplateParameters(),
-                                             computation));
-      continue;
-    }
-
-    // Look at all expansions in an expanded pack.
-    for (unsigned i = 0, n = TTP->getNumExpansionTemplateParameters();
-           i != n; ++i) {
-      LV.merge(getLVForTemplateParameterList(
-          TTP->getExpansionTemplateParameters(i), computation));
-    }
-  }
-
-  return LV;
-}
-
 static const Decl *getOutermostFuncOrBlockContext(const Decl *D) {
   const Decl *Ret = nullptr;
   const DeclContext *DC = D->getDeclContext();
@@ -368,10 +312,9 @@ LinkageComputer::getLVForTemplateArgumentList(const TemplateArgumentList &TArgs,
 
 static bool shouldConsiderTemplateVisibility(const FunctionDecl *fn,
                         const FunctionTemplateSpecializationInfo *specInfo) {
-  // Include visibility from the template parameters and arguments
-  // only if this is not an explicit instantiation or specialization
-  // with direct explicit visibility.  (Implicit instantiations won't
-  // have a direct attribute.)
+  // Include visibility from the template arguments only if this is not an
+  // explicit instantiation or specialization with direct explicit visibility.
+  // (Implicit instantiations won't have a direct attribute.)
   if (!specInfo->isExplicitInstantiationOrSpecialization())
     return true;
 
@@ -399,11 +342,6 @@ void LinkageComputer::mergeTemplateLV(
   // template declaration.
   LV.setLinkage(tempLV.getLinkage());
 
-  // Merge information from the template parameters.
-  LinkageInfo paramsLV =
-      getLVForTemplateParameterList(temp->getTemplateParameters(), computation);
-  LV.mergeMaybeWithVisibility(paramsLV, considerVisibility);
-
   // Merge information from the template arguments.
   const TemplateArgumentList &templateArgs = *specInfo->TemplateArguments;
   LinkageInfo argsLV = getLVForTemplateArgumentList(templateArgs, computation);
@@ -426,14 +364,13 @@ static bool hasDirectVisibilityAttribute(const NamedDecl *D,
 static bool shouldConsiderTemplateVisibility(
                                  const ClassTemplateSpecializationDecl *spec,
                                  LVComputationKind computation) {
-  // Include visibility from the template parameters and arguments
-  // only if this is not an explicit instantiation or specialization
-  // with direct explicit visibility (and note that implicit
-  // instantiations won't have a direct attribute).
+  // Include visibility from the template arguments only if this is not an
+  // explicit instantiation or specialization with direct explicit visibility
+  // (and note that implicit instantiations won't have a direct attribute).
   //
-  // Furthermore, we want to ignore template parameters and arguments
-  // for an explicit specialization when computing the visibility of a
-  // member thereof with explicit visibility.
+  // Furthermore, we want to ignore template arguments for an explicit
+  // specialization when computing the visibility of a member thereof with
+  // explicit visibility.
   //
   // This is a bit complex; let's unpack it.
   //
@@ -464,8 +401,6 @@ void LinkageComputer::mergeTemplateLV(
     LVComputationKind computation) {
   bool considerVisibility = shouldConsiderTemplateVisibility(spec, computation);
 
-  // Merge information from the template parameters, but ignore
-  // visibility if we're only considering template arguments.
   ClassTemplateDecl *temp = spec->getSpecializedTemplate();
   // Merge information from the template declaration.
   LinkageInfo tempLV = getLVForDecl(temp, computation);
@@ -473,11 +408,6 @@ void LinkageComputer::mergeTemplateLV(
   // template declaration.
   LV.setLinkage(tempLV.getLinkage());
 
-  LinkageInfo paramsLV =
-    getLVForTemplateParameterList(temp->getTemplateParameters(), computation);
-  LV.mergeMaybeWithVisibility(paramsLV,
-           considerVisibility && !hasExplicitVisibilityAlready(computation));
-
   // Merge information from the template arguments.  We ignore
   // template-argument visibility if we've got an explicit
   // instantiation with a visibility attribute.
@@ -521,14 +451,6 @@ void LinkageComputer::mergeTemplateLV(LinkageInfo &LV,
                                       LVComputationKind computation) {
   bool considerVisibility = shouldConsiderTemplateVisibility(spec, computation);
 
-  // Merge information from the template parameters, but ignore
-  // visibility if we're only considering template arguments.
-  VarTemplateDecl *temp = spec->getSpecializedTemplate();
-  LinkageInfo tempLV =
-    getLVForTemplateParameterList(temp->getTemplateParameters(), computation);
-  LV.mergeMaybeWithVisibility(tempLV,
-           considerVisibility && !hasExplicitVisibilityAlready(computation));
-
   // Merge information from the template arguments.  We ignore
   // template-argument visibility if we've got an explicit
   // instantiation with a visibility attribute.
@@ -858,10 +780,6 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
 
   //     - a template
   } else if (const auto *temp = dyn_cast<TemplateDecl>(D)) {
-    bool considerVisibility = !hasExplicitVisibilityAlready(computation);
-    LinkageInfo tempLV =
-      getLVForTemplateParameterList(temp->getTemplateParameters(), computation);
-    LV.mergeMaybeWithVisibility(tempLV, considerVisibility);
 
   //     An unnamed namespace or a namespace declared directly or indirectly
   //     within an unnamed namespace has internal linkage. All other namespaces
@@ -1029,14 +947,6 @@ LinkageComputer::getLVForClassMember(const NamedDecl *D,
 
   // Template members.
   } else if (const auto *temp = dyn_cast<TemplateDecl>(D)) {
-    bool considerVisibility =
-      (!LV.isVisibilityExplicit() &&
-       !classLV.isVisibilityExplicit() &&
-       !hasExplicitVisibilityAlready(computation));
-    LinkageInfo tempLV =
-      getLVForTemplateParameterList(temp->getTemplateParameters(), computation);
-    LV.mergeMaybeWithVisibility(tempLV, considerVisibility);
-
     if (const auto *redeclTemp = dyn_cast<RedeclarableTemplateDecl>(temp)) {
       if (isExplicitMemberSpecialization(redeclTemp)) {
         explicitSpecSuppressor = temp->getTemplatedDecl();
diff --git a/clang/lib/AST/Linkage.h b/clang/lib/AST/Linkage.h
index 31f384eb75d0b2e..0907bad8e656dbf 100644
--- a/clang/lib/AST/Linkage.h
+++ b/clang/lib/AST/Linkage.h
@@ -137,9 +137,6 @@ class LinkageComputer {
 
   LinkageInfo getLVForType(const Type &T, LVComputationKind computation);
 
-  LinkageInfo getLVForTemplateParameterList(const TemplateParameterList *Params,
-                                            LVComputationKind computation);
-
   LinkageInfo getLVForValue(const APValue &V, LVComputationKind computation);
 
 public:
diff --git a/clang/test/CodeGenCXX/visibility.cpp b/clang/test/CodeGenCXX/visibility.cpp
index b017bc8b52d5a5d..30f9a6005b04078 100644
--- a/clang/test/CodeGenCXX/visibility.cpp
+++ b/clang/test/CodeGenCXX/visibility.cpp
@@ -83,9 +83,6 @@ namespace test41 {
 }
 
 namespace test48 {
-  // Test that we use the visibility of struct foo when instantiating the
-  // template. Note that is a case where we disagree with gcc, it produces
-  // a default symbol.
   struct HIDDEN foo {
   };
   DEFAULT foo x;
@@ -97,7 +94,7 @@ namespace test48 {
   };
 
   bar::zed<&x> y;
-  // CHECK: _ZN6test481yE = hidden global
+  // CHECK: _ZN6test481yE = global
   // CHECK-HIDDEN: _ZN6test481yE = hidden global
 }
 
@@ -137,22 +134,22 @@ namespace test73 {
   template int HIDDEN var<&hc>;
 
   int use() { return var<&dd> + var<&hd>; }
-  // CHECK:      @_ZN6test733varIXadL_ZNS_2daEEEEE = weak_odr hidden global i32 0
+  // CHECK:      @_ZN6test733varIXadL_ZNS_2daEEEEE = weak_odr global i32 0
   // CHECK-NEXT: @_ZN6test733varIXadL_ZNS_2dbEEEEE = weak_odr global i32 0
   // CHECK-NEXT: @_ZN6test733varIXadL_ZNS_2dcEEEEE = weak_odr hidden global i32 0
   // CHECK-NEXT: @_ZN6test733varIXadL_ZNS_2haEEEEE = weak_odr hidden global i32 0
   // CHECK-NEXT: @_ZN6test733varIXadL_ZNS_2hbEEEEE = weak_odr global i32 0
   // CHECK-NEXT: @_ZN6test733varIXadL_ZNS_2hcEEEEE = weak_odr hidden global i32 0
-  // CHECK:      @_ZN6test733varIXadL_ZNS_2ddEEEEE = linkonce_odr hidden global i32 0
+  // CHECK:      @_ZN6test733varIXadL_ZNS_2ddEEEEE = linkonce_odr global i32 0
   // CHECK-NEXT: @_ZN6test733varIXadL_ZNS_2hdEEEEE = linkonce_odr hidden global i32 0
 
-  // CHECK-HIDDEN:      @_ZN6test733varIXadL_ZNS_2daEEEEE = weak_odr hidden global i32 0
+  // CHECK-HIDDEN:      @_ZN6test733varIXadL_ZNS_2daEEEEE = weak_odr global i32 0
   // CHECK-HIDDEN-NEXT: @_ZN6test733varIXadL_ZNS_2dbEEEEE = weak_odr global i32 0
   // CHECK-HIDDEN-NEXT: @_ZN6test733varIXadL_ZNS_2dcEEEEE = weak_odr hidden global i32 0
   // CHECK-HIDDEN-NEXT: @_ZN6test733varIXadL_ZNS_2haEEEEE = weak_odr hidden global i32 0
   // CHECK-HIDDEN-NEXT: @_ZN6test733varIXadL_ZNS_2hbEEEEE = weak_odr global i32 0
   // CHECK-HIDDEN-NEXT: @_ZN6test733varIXadL_ZNS_2hcEEEEE = weak_odr hidden global i32 0
-  // CHECK-HIDDEN:      @_ZN6test733varIXadL_ZNS_2ddEEEEE = linkonce_odr hidden global i32 0
+  // CHECK-HIDDEN:      @_ZN6test733varIXadL_ZNS_2ddEEEEE = linkonce_odr global i32 0
   // CHECK-HIDDEN-NEXT: @_ZN6test733varIXadL_ZNS_2hdEEEEE = linkonce_odr hidden global i32 0
 }
 
@@ -963,10 +960,9 @@ namespace test47 {
 }
 
 namespace test49 {
-  // Test that we use the visibility of struct foo when instantiating the
-  // template. Note that is a case where we disagree with gcc, it produces
-  // a default symbol.
-
+  // When instantiating the template, we constraint the visibility with the
+  // non-type template argument &x. The type of &x does not constraint the
+  // visibility.
   struct HIDDEN foo {
   };
 
@@ -979,15 +975,11 @@ namespace test49 {
   };
 
   template void bar::zed<&x>();
-  // CHECK-LABEL: define weak_odr hidden void @_ZN6test493bar3zedIXadL_ZNS_1xEEEEEvv
+  // CHECK-LABEL: define weak_odr void @_ZN6test493bar3zedIXadL_ZNS_1xEEEEEvv
   // CHECK-HIDDEN-LABEL: define weak_odr hidden void @_ZN6test493bar3zedIXadL_ZNS_1xEEEEEvv
 }
 
 namespace test50 {
-  // Test that we use the visibility of struct foo when instantiating the
-  // template. Note that is a case where we disagree with gcc, it produces
-  // a default symbol.
-
   struct HIDDEN foo {
   };
   DEFAULT foo x;
@@ -997,15 +989,11 @@ namespace test50 {
     }
   };
   template void bar<&x>::zed();
-  // CHECK-LABEL: define weak_odr hidden void @_ZN6test503barIXadL_ZNS_1xEEEE3zedEv
-  // CHECK-HIDDEN-LABEL: define weak_odr hidden void @_ZN6test503barIXadL_ZNS_1xEEEE3zedEv
+  // CHECK-LABEL: define weak_odr void @_ZN6test503barIXadL_ZNS_1xEEEE3zedEv
+  // CHECK-HIDDEN-LABEL: define weak_odr void @_ZN6test503barIXadL_ZNS_1xEEEE3zedEv
 }
 
 namespace test51 {
-  // Test that we use the visibility of struct foo when instantiating the
-  // template. Note that is a case where we disagree with gcc, it produces
-  // a default symbol.
-
   struct HIDDEN foo {};
   DEFAULT foo da, db, dc, dd, de, df;
   HIDDEN foo ha, hb, hc, hd, he, hf;
@@ -1013,8 +1001,8 @@ namespace test51 {
   void DEFAULT zed() {
   }
   template void zed<&da>();
-  // CHECK-LABEL: define weak_odr hidden void @_ZN6test513zedIXadL_ZNS_2daEEEEEvv(
-  // CHECK-HIDDEN-LABEL: define weak_odr hidden void @_ZN6test513zedIXadL_ZNS_2daEEEEEvv(
+  // CHECK-LABEL: define weak_odr void @_ZN6test513zedIXadL_ZNS_2daEEEEEvv(
+  // CHECK-HIDDEN-LABEL: define weak_odr void @_ZN6test513zedIXadL_ZNS_2daEEEEEvv(
 
   template void DEFAULT zed<&db>();
   // CHECK-LABEL: define weak_odr void @_ZN6test513zedIXadL_ZNS_2dbEEEEEvv(
@@ -1041,10 +1029,10 @@ namespace test51 {
   template void zed<&hd>();
   template void DEFAULT zed<&he>();
 #pragma GCC visibility pop
-  // CHECK-LABEL: define weak_odr hidden void @_ZN6test513zedIXadL_ZNS_2ddEEEEEvv(
+  // CHECK-LABEL: define weak_odr void @_ZN6test513zedIXadL_ZNS_2ddEEEEEvv(
   // CHECK-LABEL: define weak_odr hidden void @_ZN6test513zedIXadL_ZNS_2hdEEEEEvv(
   // CHECK-LABEL: define weak_odr void @_ZN6test513zedIXadL_ZNS_2heEEEEEvv(
-  // CHECK-HIDDEN-LABEL: define weak_odr hidden void @_ZN6test513zedIXadL_ZNS_2ddEEEEEvv(
+  // CHECK-HIDDEN-LABEL: define weak_odr void @_ZN6test513zedIXadL_ZNS_2ddEEEEEvv(
   // CHECK-HIDDEN-LABEL: define weak_odr hidden void @_ZN6test513zedIXadL_ZNS_2hdEEEEEvv(
   // CHECK-HIDDEN-LABEL: define weak_odr void @_ZN6test513zedIXadL_ZNS_2heEEEEEvv(
 
@@ -1052,17 +1040,13 @@ namespace test51 {
     zed<&df>();
     zed<&hf>();
   }
-  // CHECK-LABEL: define linkonce_odr hidden void @_ZN6test513zedIXadL_ZNS_2dfEEEEEvv(
+  // CHECK-LABEL: define linkonce_odr void @_ZN6test513zedIXadL_ZNS_2dfEEEEEvv(
   // CHECK-LABEL: define linkonce_odr hidden void @_ZN6test513zedIXadL_ZNS_2hfEEEEEvv(
-  // CHECK-HIDDEN-LABEL: define linkonce_odr hidden void @_ZN6test513zedIXadL_ZNS_2dfEEEEEvv(
+  // CHECK-HIDDEN-LABEL: define linkonce_odr void @_ZN6test513zedIXadL_ZNS_2dfEEEEEvv(
   // CHECK-HIDDEN-LABEL: define linkonce_odr hidden void @_ZN6test513zedIXadL_ZNS_2hfEEEEEvv(
 }
 
 namespace test52 {
-  // Test that we use the linkage of struct foo when instantiating the
-  // template. Note that is a case where we disagree with gcc, it produces
-  // an external symbol.
-
   namespace {
     struct foo {
     };
@@ -1273,8 +1257,8 @@ namespace test63 {
     A::foo<E0>();
     A::B<E0>::foo();
   }
-  // CHECK-LABEL: define linkonce_odr hidden void @_ZN6test631A3fooILNS_1EE0EEEvv()
-  // CHECK-LABEL: define linkonce_odr hidden void @_ZN6test631A1BILNS_1EE0EE3fooEv()
+  // CHECK-LABEL: define linkonce_odr void @_ZN6test631A3fooILNS_1EE0EEEvv()
+  // CHECK-LABEL: define linkonce_odr void @_ZN6test631A1BILNS_1EE0EE3fooEv()
 }
 
 // Don't ignore the visibility of template arguments just because we

``````````

</details>


https://github.com/llvm/llvm-project/pull/72092


More information about the cfe-commits mailing list