When attribute 'optnone' appears on the same declaration with a
conflicting attribute, warn about the conflict and pick a "winning"
attribute to preserve, instead of emitting an error.  This matches the
behavior when the conflicting attributes are on different declarations.

Along the way I discovered that conflicts involving __forceinline were
reported as 'always_inline' (alternate spelling, same attribute) so
fixed that up to report the attribute as spelled in the source.

llvm-svn: 225813
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2ba74b1..74efa60 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2009,6 +2009,7 @@
   SectionAttr *mergeSectionAttr(Decl *D, SourceRange Range, StringRef Name,
                                 unsigned AttrSpellingListIndex);
   AlwaysInlineAttr *mergeAlwaysInlineAttr(Decl *D, SourceRange Range,
+                                          IdentifierInfo *Ident,
                                           unsigned AttrSpellingListIndex);
   MinSizeAttr *mergeMinSizeAttr(Decl *D, SourceRange Range,
                                 unsigned AttrSpellingListIndex);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index baa6822..7d1e28e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2155,7 +2155,9 @@
                                        AttrSpellingListIndex,
                                        IA->getSemanticSpelling());
   else if (const auto *AA = dyn_cast<AlwaysInlineAttr>(Attr))
-    NewAttr = S.mergeAlwaysInlineAttr(D, AA->getRange(), AttrSpellingListIndex);
+    NewAttr = S.mergeAlwaysInlineAttr(D, AA->getRange(),
+                                      &S.Context.Idents.get(AA->getSpelling()),
+                                      AttrSpellingListIndex);
   else if (const auto *MA = dyn_cast<MinSizeAttr>(Attr))
     NewAttr = S.mergeMinSizeAttr(D, MA->getRange(), AttrSpellingListIndex);
   else if (const auto *OA = dyn_cast<OptimizeNoneAttr>(Attr))
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index aec387e..17a849e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3141,9 +3141,10 @@
 }
 
 AlwaysInlineAttr *Sema::mergeAlwaysInlineAttr(Decl *D, SourceRange Range,
+                                              IdentifierInfo *Ident,
                                               unsigned AttrSpellingListIndex) {
   if (OptimizeNoneAttr *Optnone = D->getAttr<OptimizeNoneAttr>()) {
-    Diag(Range.getBegin(), diag::warn_attribute_ignored) << "'always_inline'";
+    Diag(Range.getBegin(), diag::warn_attribute_ignored) << Ident;
     Diag(Optnone->getLocation(), diag::note_conflicting_attribute);
     return nullptr;
   }
@@ -3191,34 +3192,23 @@
 
 static void handleAlwaysInlineAttr(Sema &S, Decl *D,
                                    const AttributeList &Attr) {
-  if (checkAttrMutualExclusion<OptimizeNoneAttr>(S, D, Attr))
-    return;
-
-  D->addAttr(::new (S.Context)
-             AlwaysInlineAttr(Attr.getRange(), S.Context,
-                              Attr.getAttributeSpellingListIndex()));
+  if (AlwaysInlineAttr *Inline = S.mergeAlwaysInlineAttr(
+          D, Attr.getRange(), Attr.getName(),
+          Attr.getAttributeSpellingListIndex()))
+    D->addAttr(Inline);
 }
 
-static void handleMinSizeAttr(Sema &S, Decl *D,
-                              const AttributeList &Attr) {
-  if (checkAttrMutualExclusion<OptimizeNoneAttr>(S, D, Attr))
-    return;
-
-  D->addAttr(::new (S.Context)
-             MinSizeAttr(Attr.getRange(), S.Context,
-                         Attr.getAttributeSpellingListIndex()));
+static void handleMinSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  if (MinSizeAttr *MinSize = S.mergeMinSizeAttr(
+          D, Attr.getRange(), Attr.getAttributeSpellingListIndex()))
+    D->addAttr(MinSize);
 }
 
 static void handleOptimizeNoneAttr(Sema &S, Decl *D,
                                    const AttributeList &Attr) {
-  if (checkAttrMutualExclusion<AlwaysInlineAttr>(S, D, Attr))
-    return;
-  if (checkAttrMutualExclusion<MinSizeAttr>(S, D, Attr))
-    return;
-
-  D->addAttr(::new (S.Context)
-             OptimizeNoneAttr(Attr.getRange(), S.Context,
-                              Attr.getAttributeSpellingListIndex()));
+  if (OptimizeNoneAttr *Optnone = S.mergeOptimizeNoneAttr(
+          D, Attr.getRange(), Attr.getAttributeSpellingListIndex()))
+    D->addAttr(Optnone);
 }
 
 static void handleGlobalAttr(Sema &S, Decl *D, const AttributeList &Attr) {
diff --git a/clang/test/SemaCXX/attr-optnone.cpp b/clang/test/SemaCXX/attr-optnone.cpp
index 89ef6c5..58776ca 100644
--- a/clang/test/SemaCXX/attr-optnone.cpp
+++ b/clang/test/SemaCXX/attr-optnone.cpp
@@ -3,8 +3,8 @@
 int foo() __attribute__((optnone));
 int bar() __attribute__((optnone)) __attribute__((noinline));
 
-int baz() __attribute__((always_inline)) __attribute__((optnone)); // expected-error{{'always_inline' and 'optnone' attributes are not compatible}}
-int quz() __attribute__((optnone)) __attribute__((always_inline)); // expected-error{{'optnone' and 'always_inline' attributes are not compatible}}
+int baz() __attribute__((always_inline)) __attribute__((optnone)); // expected-warning{{'always_inline' attribute ignored}} expected-note{{conflicting attribute is here}}
+int quz() __attribute__((optnone)) __attribute__((always_inline)); // expected-warning{{'always_inline' attribute ignored}} expected-note{{conflicting attribute is here}}
 
 __attribute__((always_inline)) int baz1(); // expected-warning{{'always_inline' attribute ignored}}
 __attribute__((optnone)) int baz1() { return 1; } // expected-note{{conflicting attribute is here}}
@@ -12,8 +12,8 @@
 __attribute__((optnone)) int quz1(); // expected-note{{conflicting attribute is here}}
 __attribute__((always_inline)) int quz1() { return 1; } // expected-warning{{'always_inline' attribute ignored}}
 
-int bay() __attribute__((minsize)) __attribute__((optnone)); // expected-error{{'minsize' and 'optnone' attributes are not compatible}}
-int quy() __attribute__((optnone)) __attribute__((minsize)); // expected-error{{'optnone' and 'minsize' attributes are not compatible}}
+int bay() __attribute__((minsize)) __attribute__((optnone)); // expected-warning{{'minsize' attribute ignored}} expected-note{{conflicting}}
+int quy() __attribute__((optnone)) __attribute__((minsize)); // expected-warning{{'minsize' attribute ignored}} expected-note{{conflicting}}
 
 __attribute__((minsize)) int bay1(); // expected-warning{{'minsize' attribute ignored}}
 __attribute__((optnone)) int bay1() { return 1; } // expected-note{{conflicting attribute is here}}
@@ -27,8 +27,13 @@
 __attribute__((optnone)) // expected-note 2 {{conflicting}}
 void bay2() {}
 
-__forceinline __attribute__((optnone)) int bax(); // expected-error{{'__forceinline' and 'optnone' attributes are not compatible}}
-__attribute__((optnone)) __forceinline int qux(); // expected-error{{'optnone' and '__forceinline' attributes are not compatible}}
+__forceinline __attribute__((optnone)) int bax(); // expected-warning{{'__forceinline' attribute ignored}} expected-note{{conflicting}}
+__attribute__((optnone)) __forceinline int qux(); // expected-warning{{'__forceinline' attribute ignored}} expected-note{{conflicting}}
+
+__forceinline int bax2(); // expected-warning{{'__forceinline' attribute ignored}}
+__attribute__((optnone)) int bax2() { return 1; } // expected-note{{conflicting}}
+__attribute__((optnone)) int qux2(); // expected-note{{conflicting}}
+__forceinline int qux2() { return 1; } // expected-warning{{'__forceinline' attribute ignored}}
 
 int globalVar __attribute__((optnone)); // expected-warning{{'optnone' attribute only applies to functions}}
 
@@ -50,8 +55,8 @@
 [[clang::optnone]]
 int bar2() __attribute__((noinline));
 
-[[clang::optnone]]
-int baz2() __attribute__((always_inline)); // expected-error{{'always_inline' and 'optnone' attributes are not compatible}}
+[[clang::optnone]] // expected-note {{conflicting}}
+int baz2() __attribute__((always_inline)); // expected-warning{{'always_inline' attribute ignored}}
 
 [[clang::optnone]] int globalVar2; //expected-warning{{'optnone' attribute only applies to functions}}