[cfe-commits] r156531 - in /cfe/trunk: include/clang/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclAttr.cpp lib/Sema/TargetAttributesSema.cpp test/Index/complete-with-annotations.cpp test/Sema/attr-visibility.c test/Sema/dllimport-dllexport.c

Rafael Espindola rafael.espindola at gmail.com
Wed May 9 19:50:16 PDT 2012


Author: rafael
Date: Wed May  9 21:50:16 2012
New Revision: 156531

URL: http://llvm.org/viewvc/llvm-project?rev=156531&view=rev
Log:
Fix an old (2009) FIXME:

// FIXME: This needs to happen before we merge declarations. Then,
// let attribute merging cope with attribute conflicts.

This was already being done for variables, but for functions we were merging
then first and then applying the attributes. To avoid duplicating merging
logic, some of the helpers in SemaDeclAttr.cpp become methods that can
handle merging two attributes in one decl or inheriting attributes from one
decl to another.

With this change we are now able to produce errors for variables with
incompatible visibility attributes or warn about unused dllimports in
variables.

This changes the attribute list iteration back to being in reverse source
code order, as that matches what decl merging does and avoids differentiating
the two cases is the merge*Attr methods.

Modified:
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/lib/Sema/TargetAttributesSema.cpp
    cfe/trunk/test/Index/complete-with-annotations.cpp
    cfe/trunk/test/Sema/attr-visibility.c
    cfe/trunk/test/Sema/dllimport-dllexport.c

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=156531&r1=156530&r2=156531&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed May  9 21:50:16 2012
@@ -1560,6 +1560,18 @@
   TypedefDecl *ParseTypedefDecl(Scope *S, Declarator &D, QualType T,
                                 TypeSourceInfo *TInfo);
   bool isIncompatibleTypedef(TypeDecl *Old, TypedefNameDecl *New);
+
+  /// Attribute merging methods. Return true if a new attribute was added.
+  bool mergeAvailabilityAttr(Decl *D, SourceRange Range, bool Inherited,
+                             IdentifierInfo *Platform, VersionTuple Introduced,
+                             VersionTuple Deprecated, VersionTuple Obsoleted,
+                             bool IsUnavailable, StringRef Message);
+  bool mergeVisibilityAttr(Decl *D, SourceRange Range,
+                           bool Inherited, VisibilityAttr::VisibilityType Vis);
+  bool mergeDLLImportAttr(Decl *D, SourceRange Range, bool Inherited);
+  bool mergeDLLExportAttr(Decl *D, SourceRange Range, bool Inherited);
+  bool mergeDeclAttribute(Decl *New, InheritableAttr *Attr);
+
   void mergeDeclAttributes(Decl *New, Decl *Old, bool MergeDeprecation = true);
   void MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls);
   bool MergeFunctionDecl(FunctionDecl *New, Decl *Old, Scope *S);

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=156531&r1=156530&r2=156531&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed May  9 21:50:16 2012
@@ -1658,6 +1658,32 @@
   return false;
 }
 
+bool Sema::mergeDeclAttribute(Decl *D, InheritableAttr *Attr) {
+  if (AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attr))
+    return mergeAvailabilityAttr(D, AA->getRange(), true, AA->getPlatform(),
+                                 AA->getIntroduced(), AA->getDeprecated(),
+                                 AA->getObsoleted(), AA->getUnavailable(),
+                                 AA->getMessage());
+
+  if (VisibilityAttr *VA = dyn_cast<VisibilityAttr>(Attr))
+    return mergeVisibilityAttr(D, VA->getRange(), true, VA->getVisibility());
+
+  if (DLLImportAttr *ImportA = dyn_cast<DLLImportAttr>(Attr))
+    return mergeDLLImportAttr(D, ImportA->getRange(), true);
+
+  if (DLLExportAttr *ExportA = dyn_cast<DLLExportAttr>(Attr))
+    return mergeDLLExportAttr(D, ExportA->getRange(), true);
+
+  if (!DeclHasAttr(D, Attr)) {
+    InheritableAttr *NewAttr = cast<InheritableAttr>(Attr->clone(Context));
+    NewAttr->setInherited(true);
+    D->addAttr(NewAttr);
+    return true;
+  }
+
+  return false;
+}
+
 /// mergeDeclAttributes - Copy attributes from the Old decl to the New one.
 void Sema::mergeDeclAttributes(Decl *New, Decl *Old,
                                bool MergeDeprecation) {
@@ -1681,12 +1707,8 @@
          isa<AvailabilityAttr>(*i)))
       continue;
 
-    if (!DeclHasAttr(New, *i)) {
-      InheritableAttr *newAttr = cast<InheritableAttr>((*i)->clone(Context));
-      newAttr->setInherited(true);
-      New->addAttr(newAttr);
+    if (mergeDeclAttribute(New, *i))
       foundAny = true;
-    }
   }
 
   if (!foundAny) New->dropAttrs();
@@ -5376,6 +5398,10 @@
     NewFD->setInvalidDecl();
   }
 
+  // Handle attributes.
+  ProcessDeclAttributes(S, NewFD, D,
+                        /*NonInheritable=*/false, /*Inheritable=*/true);
+
   if (!getLangOpts().CPlusPlus) {
     // Perform semantic checking on the function declaration.
     bool isExplicitSpecialization=false;
@@ -5623,14 +5649,6 @@
         << D.getCXXScopeSpec().getRange();
     }
   }
- 
- 
-  // Handle attributes. We need to have merged decls when handling attributes
-  // (for example to check for conflicts, etc).
-  // FIXME: This needs to happen before we merge declarations. Then,
-  // let attribute merging cope with attribute conflicts.
-  ProcessDeclAttributes(S, NewFD, D,
-                        /*NonInheritable=*/false, /*Inheritable=*/true);
 
   // attributes declared post-definition are currently ignored
   // FIXME: This should happen during attribute merging
@@ -8715,6 +8733,9 @@
       InFunctionDeclarator && Name)
     DeclsInPrototypeScope.push_back(New);
 
+  if (PrevDecl)
+    mergeDeclAttributes(New, PrevDecl);
+
   OwnedDecl = true;
   return New;
 }

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=156531&r1=156530&r2=156531&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Wed May  9 21:50:16 2012
@@ -1729,76 +1729,101 @@
   return false;
 }
 
-static void mergeAvailabilityAttr(Sema &S, Decl *D, SourceRange Range,
-                                  IdentifierInfo *Platform,
-                                  VersionTuple Introduced,
-                                  VersionTuple Deprecated,
-                                  VersionTuple Obsoleted,
-                                  bool IsUnavailable,
-                                  StringRef Message) {
-  VersionTuple MergedIntroduced;
-  VersionTuple MergedDeprecated;
-  VersionTuple MergedObsoleted;
+bool Sema::mergeAvailabilityAttr(Decl *D, SourceRange Range,
+                                 bool Inherited,
+                                 IdentifierInfo *Platform,
+                                 VersionTuple Introduced,
+                                 VersionTuple Deprecated,
+                                 VersionTuple Obsoleted,
+                                 bool IsUnavailable,
+                                 StringRef Message) {
+  VersionTuple MergedIntroduced = Introduced;
+  VersionTuple MergedDeprecated = Deprecated;
+  VersionTuple MergedObsoleted = Obsoleted;
   bool FoundAny = false;
 
-  for (specific_attr_iterator<AvailabilityAttr>
-         i = D->specific_attr_begin<AvailabilityAttr>(),
-         e = D->specific_attr_end<AvailabilityAttr>();
-       i != e ; ++i) {
-    const AvailabilityAttr *OldAA = *i;
-    IdentifierInfo *OldPlatform = OldAA->getPlatform();
-    if (OldPlatform != Platform)
-      continue;
-    FoundAny = true;
-    VersionTuple OldIntroduced = OldAA->getIntroduced();
-    VersionTuple OldDeprecated = OldAA->getDeprecated();
-    VersionTuple OldObsoleted = OldAA->getObsoleted();
-    bool OldIsUnavailable = OldAA->getUnavailable();
-    StringRef OldMessage = OldAA->getMessage();
-
-    if ((!OldIntroduced.empty() && !Introduced.empty() &&
-         OldIntroduced != Introduced) ||
-        (!OldDeprecated.empty() && !Deprecated.empty() &&
-         OldDeprecated != Deprecated) ||
-        (!OldObsoleted.empty() && !Obsoleted.empty() &&
-         OldObsoleted != Obsoleted) ||
-        (OldIsUnavailable != IsUnavailable) ||
-        (OldMessage != Message)) {
-      S.Diag(Range.getBegin(), diag::warn_mismatched_availability);
-      S.Diag(OldAA->getLocation(), diag::note_previous_attribute);
-      return;
-    }
-    if (MergedIntroduced.empty())
-      MergedIntroduced = OldIntroduced;
-    if (MergedDeprecated.empty())
-      MergedDeprecated = OldDeprecated;
-    if (MergedObsoleted.empty())
-      MergedObsoleted = OldObsoleted;
+  if (D->hasAttrs()) {
+    AttrVec &Attrs = D->getAttrs();
+    for (unsigned i = 0, e = Attrs.size(); i != e;) {
+      const AvailabilityAttr *OldAA = dyn_cast<AvailabilityAttr>(Attrs[i]);
+      if (!OldAA) {
+        ++i;
+        continue;
+      }
+
+      IdentifierInfo *OldPlatform = OldAA->getPlatform();
+      if (OldPlatform != Platform) {
+        ++i;
+        continue;
+      }
+
+      FoundAny = true;
+      VersionTuple OldIntroduced = OldAA->getIntroduced();
+      VersionTuple OldDeprecated = OldAA->getDeprecated();
+      VersionTuple OldObsoleted = OldAA->getObsoleted();
+      bool OldIsUnavailable = OldAA->getUnavailable();
+      StringRef OldMessage = OldAA->getMessage();
+
+      if ((!OldIntroduced.empty() && !Introduced.empty() &&
+           OldIntroduced != Introduced) ||
+          (!OldDeprecated.empty() && !Deprecated.empty() &&
+           OldDeprecated != Deprecated) ||
+          (!OldObsoleted.empty() && !Obsoleted.empty() &&
+           OldObsoleted != Obsoleted) ||
+          (OldIsUnavailable != IsUnavailable) ||
+          (OldMessage != Message)) {
+        Diag(OldAA->getLocation(), diag::warn_mismatched_availability);
+        Diag(Range.getBegin(), diag::note_previous_attribute);
+        Attrs.erase(Attrs.begin() + i);
+        --e;
+        continue;
+      }
+
+      VersionTuple MergedIntroduced2 = MergedIntroduced;
+      VersionTuple MergedDeprecated2 = MergedDeprecated;
+      VersionTuple MergedObsoleted2 = MergedObsoleted;
+
+      if (MergedIntroduced2.empty())
+        MergedIntroduced2 = OldIntroduced;
+      if (MergedDeprecated2.empty())
+        MergedDeprecated2 = OldDeprecated;
+      if (MergedObsoleted2.empty())
+        MergedObsoleted2 = OldObsoleted;
+
+      if (checkAvailabilityAttr(*this, OldAA->getRange(), Platform,
+                                MergedIntroduced2, MergedDeprecated2,
+                                MergedObsoleted2)) {
+        Attrs.erase(Attrs.begin() + i);
+        --e;
+        continue;
+      }
+
+      MergedIntroduced = MergedIntroduced2;
+      MergedDeprecated = MergedDeprecated2;
+      MergedObsoleted = MergedObsoleted2;
+      ++i;
+    }
   }
 
   if (FoundAny &&
       MergedIntroduced == Introduced &&
       MergedDeprecated == Deprecated &&
       MergedObsoleted == Obsoleted)
-    return;
-
-  if (MergedIntroduced.empty())
-    MergedIntroduced = Introduced;
-  if (MergedDeprecated.empty())
-    MergedDeprecated = Deprecated;
-  if (MergedObsoleted.empty())
-    MergedObsoleted = Obsoleted;
+    return false;
 
-  if (!checkAvailabilityAttr(S, Range, Platform, MergedIntroduced,
+  if (!checkAvailabilityAttr(*this, Range, Platform, MergedIntroduced,
                              MergedDeprecated, MergedObsoleted)) {
-    D->addAttr(::new (S.Context) AvailabilityAttr(Range, S.Context,
-                                                  Platform,
-                                                  Introduced,
-                                                  Deprecated,
-                                                  Obsoleted,
-                                                  IsUnavailable,
-                                                  Message));
+    AvailabilityAttr *Attr =
+      ::new (Context) AvailabilityAttr(Range, Context, Platform,
+                                       Introduced, Deprecated,
+                                       Obsoleted, IsUnavailable, Message);
+
+    if (Inherited)
+      Attr->setInherited(true);
+    D->addAttr(Attr);
+    return true;
   }
+  return false;
 }
 
 static void handleAvailabilityAttr(Sema &S, Decl *D,
@@ -1820,13 +1845,32 @@
   if (SE)
     Str = SE->getString();
 
-  mergeAvailabilityAttr(S, D, Attr.getRange(),
-                        Platform,
-                        Introduced.Version,
-                        Deprecated.Version,
-                        Obsoleted.Version,
-                        IsUnavailable,
-                        Str);
+  S.mergeAvailabilityAttr(D, Attr.getRange(),
+                          false, Platform,
+                          Introduced.Version,
+                          Deprecated.Version,
+                          Obsoleted.Version,
+                          IsUnavailable,
+                          Str);
+}
+
+bool Sema::mergeVisibilityAttr(Decl *D, SourceRange Range,
+                               bool Inherited,
+                               VisibilityAttr::VisibilityType Vis) {
+  VisibilityAttr *ExistingAttr = D->getAttr<VisibilityAttr>();
+  if (ExistingAttr) {
+    VisibilityAttr::VisibilityType ExistingVis = ExistingAttr->getVisibility();
+    if (ExistingVis == Vis)
+      return false;
+    Diag(ExistingAttr->getLocation(), diag::err_mismatched_visibility);
+    Diag(Range.getBegin(), diag::note_previous_attribute);
+    D->dropAttr<VisibilityAttr>();
+  }
+  VisibilityAttr *Attr = ::new (Context) VisibilityAttr(Range, Context, Vis);
+  if (Inherited)
+    Attr->setInherited(true);
+  D->addAttr(Attr);
+  return true;
 }
 
 static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) {
@@ -1867,25 +1911,7 @@
     return;
   }
 
-  // Find the last Decl that has an attribute.
-  VisibilityAttr *PrevAttr = 0;
-  assert(D->redecls_begin() == D);
-  for (Decl::redecl_iterator I = D->redecls_begin(), E = D->redecls_end();
-       I != E; ++I) {
-    PrevAttr = I->getAttr<VisibilityAttr>() ;
-    if (PrevAttr)
-      break;
-  }
-
-  if (PrevAttr) {
-    VisibilityAttr::VisibilityType PrevVisibility = PrevAttr->getVisibility();
-    if (PrevVisibility != type) {
-      S.Diag(Attr.getLoc(), diag::err_mismatched_visibility);
-      S.Diag(PrevAttr->getLocation(), diag::note_previous_attribute);
-      return;
-    }
-  }
-  D->addAttr(::new (S.Context) VisibilityAttr(Attr.getRange(), S.Context, type));
+  S.mergeVisibilityAttr(D, Attr.getRange(), false, type);
 }
 
 static void handleObjCMethodFamilyAttr(Sema &S, Decl *decl,
@@ -3988,11 +4014,9 @@
                                     bool NonInheritable, bool Inheritable) {
   SmallVector<const AttributeList*, 4> attrs;
   for (const AttributeList* l = AttrList; l; l = l->getNext()) {
+    ProcessDeclAttribute(*this, S, D, *l, NonInheritable, Inheritable);
     attrs.push_back(l);
   }
-  for (int i = attrs.size() - 1; i >= 0; --i) {
-    ProcessDeclAttribute(*this, S, D, *attrs[i], NonInheritable, Inheritable);
-  }
 
   // GCC accepts
   // static int a9 __attribute__((weakref));

Modified: cfe/trunk/lib/Sema/TargetAttributesSema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TargetAttributesSema.cpp?rev=156531&r1=156530&r2=156531&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/TargetAttributesSema.cpp (original)
+++ cfe/trunk/lib/Sema/TargetAttributesSema.cpp Wed May  9 21:50:16 2012
@@ -151,6 +151,24 @@
                                                            S.Context));
 }
 
+bool Sema::mergeDLLImportAttr(Decl *D, SourceRange Range, bool Inherited) {
+  if (D->hasAttr<DLLExportAttr>()) {
+    Diag(Range.getBegin(), diag::warn_attribute_ignored) << "dllimport";
+    return false;
+  }
+
+  if (D->hasAttr<DLLImportAttr>())
+    return false;
+
+  DLLImportAttr *Attr =
+    ::new (Context) DLLImportAttr(Range, Context);
+  if (Inherited)
+    Attr->setInherited(true);
+  D->addAttr(Attr);
+
+  return true;
+}
+
 static void HandleDLLImportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
   // check the attribute arguments.
   if (Attr.getNumArgs() != 0) {
@@ -159,13 +177,8 @@
   }
 
   // Attribute can be applied only to functions or variables.
-  if (isa<VarDecl>(D)) {
-    D->addAttr(::new (S.Context) DLLImportAttr(Attr.getLoc(), S.Context));
-    return;
-  }
-
   FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
-  if (!FD) {
+  if (!FD && !isa<VarDecl>(D)) {
     // Apparently Visual C++ thinks it is okay to not emit a warning
     // in this case, so only emit a warning when -fms-extensions is not
     // specified.
@@ -177,17 +190,30 @@
 
   // Currently, the dllimport attribute is ignored for inlined functions.
   // Warning is emitted.
-  if (FD->isInlineSpecified()) {
+  if (FD && FD->isInlineSpecified()) {
     S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport";
     return;
   }
 
-  if (D->getAttr<DLLExportAttr>()) {
-    S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllimport";
-    return;
+  S.mergeDLLImportAttr(D, Attr.getRange(), false);
+}
+
+bool Sema::mergeDLLExportAttr(Decl *D, SourceRange Range, bool Inherited) {
+  if (DLLImportAttr *Import = D->getAttr<DLLImportAttr>()) {
+    Diag(Import->getLocation(), diag::warn_attribute_ignored) << "dllimport";
+    D->dropAttr<DLLImportAttr>();
   }
 
-  D->addAttr(::new (S.Context) DLLImportAttr(Attr.getLoc(), S.Context));
+  if (D->hasAttr<DLLExportAttr>())
+    return false;
+
+  DLLExportAttr *Attr =
+    ::new (Context) DLLExportAttr(Range, Context);
+  if (Inherited)
+    Attr->setInherited(true);
+  D->addAttr(Attr);
+
+  return true;
 }
 
 static void HandleDLLExportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
@@ -198,13 +224,8 @@
   }
 
   // Attribute can be applied only to functions or variables.
-  if (isa<VarDecl>(D)) {
-    D->addAttr(::new (S.Context) DLLExportAttr(Attr.getLoc(), S.Context));
-    return;
-  }
-
   FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
-  if (!FD) {
+  if (!FD && !isa<VarDecl>(D)) {
     S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
       << Attr.getName() << 2 /*variable and function*/;
     return;
@@ -212,18 +233,13 @@
 
   // Currently, the dllexport attribute is ignored for inlined functions, unless
   // the -fkeep-inline-functions flag has been used. Warning is emitted;
-  if (FD->isInlineSpecified()) {
+  if (FD && FD->isInlineSpecified()) {
     // FIXME: ... unless the -fkeep-inline-functions flag has been used.
     S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "dllexport";
     return;
   }
 
-  if (DLLImportAttr *Import = D->getAttr<DLLImportAttr>()) {
-    S.Diag(Import->getLocation(), diag::warn_attribute_ignored) << "dllimport";
-    D->dropAttr<DLLImportAttr>();
-  }
-
-  D->addAttr(::new (S.Context) DLLExportAttr(Attr.getLoc(), S.Context));
+  S.mergeDLLExportAttr(D, Attr.getRange(), false);
 }
 
 namespace {

Modified: cfe/trunk/test/Index/complete-with-annotations.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/complete-with-annotations.cpp?rev=156531&r1=156530&r2=156531&view=diff
==============================================================================
--- cfe/trunk/test/Index/complete-with-annotations.cpp (original)
+++ cfe/trunk/test/Index/complete-with-annotations.cpp Wed May  9 21:50:16 2012
@@ -14,7 +14,7 @@
 }
 
 // CHECK: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34)
-// CHECK: FieldDecl:{ResultType int}{TypedText field} (35) ("one", "two", "three")
+// CHECK: FieldDecl:{ResultType int}{TypedText field} (35) ("three", "two", "one")
 // CHECK: CXXMethod:{ResultType void}{TypedText func2}{LeftParen (}{RightParen )} (34) ("some annotation")
 // CHECK: FieldDecl:{ResultType int}{TypedText member2} (35) ("another annotation", "some annotation")
 // CHECK: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (34)

Modified: cfe/trunk/test/Sema/attr-visibility.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/attr-visibility.c?rev=156531&r1=156530&r2=156531&view=diff
==============================================================================
--- cfe/trunk/test/Sema/attr-visibility.c (original)
+++ cfe/trunk/test/Sema/attr-visibility.c Wed May  9 21:50:16 2012
@@ -17,3 +17,6 @@
 
 void test6() __attribute__((visibility("hidden"), // expected-note {{previous attribute is here}}
                             visibility("default"))); // expected-error {{visibility does not match previous declaration}}
+
+extern int test7 __attribute__((visibility("default"))); // expected-note {{previous attribute is here}}
+extern int test7 __attribute__((visibility("hidden"))); // expected-error {{visibility does not match previous declaration}}

Modified: cfe/trunk/test/Sema/dllimport-dllexport.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/dllimport-dllexport.c?rev=156531&r1=156530&r2=156531&view=diff
==============================================================================
--- cfe/trunk/test/Sema/dllimport-dllexport.c (original)
+++ cfe/trunk/test/Sema/dllimport-dllexport.c Wed May  9 21:50:16 2012
@@ -38,3 +38,6 @@
 
 void __attribute__((dllimport)) foo13(); // expected-warning{{dllimport attribute ignored}}
 void __attribute__((dllexport)) foo13();
+
+extern int foo14 __attribute__((dllexport));
+extern int foo14 __attribute__((dllimport));  // expected-warning{{dllimport attribute ignored}}





More information about the cfe-commits mailing list