PR c++/83871 - wrong code for attribute const and pure on distinct template specializ...
[platform/upstream/gcc.git] / gcc / cp / decl.c
index cf91773..6af774f 100644 (file)
@@ -1405,9 +1405,18 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
               " literal operator template %qD", newdecl, olddecl);
     }
 
+  /* True to merge attributes between the declarations, false to
+     set OLDDECL's attributes to those of NEWDECL (for template
+     explicit specializations that specify their own attributes
+     independent of those specified for the primary template).  */
+  const bool merge_attr = (TREE_CODE (newdecl) != FUNCTION_DECL
+                          || !DECL_TEMPLATE_SPECIALIZATION (newdecl)
+                          || DECL_TEMPLATE_SPECIALIZATION (olddecl));
+
   if (DECL_P (olddecl)
       && TREE_CODE (newdecl) == FUNCTION_DECL
       && TREE_CODE (olddecl) == FUNCTION_DECL
+      && merge_attr
       && diagnose_mismatched_attributes (olddecl, newdecl))
     {
       if (DECL_INITIAL (olddecl))
@@ -1969,10 +1978,13 @@ next_arg:;
       DECL_ORIGINAL_TYPE (newdecl) = DECL_ORIGINAL_TYPE (olddecl);
     }
 
-  /* Copy all the DECL_... slots specified in the new decl
-     except for any that we copy here from the old type.  */
-  DECL_ATTRIBUTES (newdecl)
-    = (*targetm.merge_decl_attributes) (olddecl, newdecl);
+  /* Copy all the DECL_... slots specified in the new decl except for
+     any that we copy here from the old type.  */
+  if (merge_attr)
+    DECL_ATTRIBUTES (newdecl)
+      = (*targetm.merge_decl_attributes) (olddecl, newdecl);
+  else
+    DECL_ATTRIBUTES (olddecl) = DECL_ATTRIBUTES (newdecl);
 
   if (DECL_DECLARES_FUNCTION_P (olddecl) && DECL_DECLARES_FUNCTION_P (newdecl))
     {
@@ -2099,9 +2111,10 @@ next_arg:;
                  }
            }
        }
-      else
-       /* Merge the data types specified in the two decls.  */
+      else if (merge_attr)
        newtype = merge_types (TREE_TYPE (newdecl), TREE_TYPE (olddecl));
+      else
+       newtype = TREE_TYPE (newdecl);
 
       if (VAR_P (newdecl))
        {
@@ -2165,14 +2178,6 @@ next_arg:;
          && !(processing_template_decl && uses_template_parms (newdecl)))
        layout_decl (newdecl, 0);
 
-      /* Merge the type qualifiers.  */
-      if (TREE_READONLY (newdecl))
-       TREE_READONLY (olddecl) = 1;
-      if (TREE_THIS_VOLATILE (newdecl))
-       TREE_THIS_VOLATILE (olddecl) = 1;
-      if (TREE_NOTHROW (newdecl))
-       TREE_NOTHROW (olddecl) = 1;
-
       /* Merge deprecatedness.  */
       if (TREE_DEPRECATED (newdecl))
        TREE_DEPRECATED (olddecl) = 1;
@@ -2190,6 +2195,15 @@ next_arg:;
            DECL_FUNCTION_SPECIFIC_OPTIMIZATION (newdecl)
              = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (olddecl);
        }
+      else
+       {
+         /* Merge the const type qualifier.  */
+         if (TREE_READONLY (newdecl))
+           TREE_READONLY (olddecl) = 1;
+         /* Merge the volatile type qualifier.  */
+         if (TREE_THIS_VOLATILE (newdecl))
+           TREE_THIS_VOLATILE (olddecl) = 1;
+       }
 
       /* Merge the initialization information.  */
       if (DECL_INITIAL (newdecl) == NULL_TREE
@@ -2209,14 +2223,29 @@ next_arg:;
          DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
            |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
          DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl);
-         TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
-         TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);
-         DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
          DECL_IS_OPERATOR_NEW (newdecl) |= DECL_IS_OPERATOR_NEW (olddecl);
-         DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl);
-         TREE_READONLY (newdecl) |= TREE_READONLY (olddecl);
-         DECL_LOOPING_CONST_OR_PURE_P (newdecl) 
+         DECL_LOOPING_CONST_OR_PURE_P (newdecl)
            |= DECL_LOOPING_CONST_OR_PURE_P (olddecl);
+
+         if (merge_attr)
+           {
+             TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
+             TREE_THIS_VOLATILE (olddecl) |= TREE_THIS_VOLATILE (newdecl);
+             TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);
+             TREE_NOTHROW (olddecl) |= TREE_NOTHROW (newdecl);
+             TREE_READONLY (newdecl) |= TREE_READONLY (olddecl);
+             DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
+             DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl);
+           }
+         else
+           {
+             /* Merge the noreturn bit.  */
+             TREE_THIS_VOLATILE (olddecl) = TREE_THIS_VOLATILE (newdecl);
+             TREE_READONLY (olddecl) = TREE_READONLY (newdecl);
+             TREE_NOTHROW (olddecl) = TREE_NOTHROW (newdecl);
+             DECL_IS_MALLOC (olddecl) = DECL_IS_MALLOC (newdecl);
+             DECL_PURE_P (olddecl) = DECL_PURE_P (newdecl);
+           }
          /* Keep the old RTL.  */
          COPY_DECL_RTL (olddecl, newdecl);
        }
@@ -2381,17 +2410,30 @@ next_arg:;
          /* [temp.expl.spec/14] We don't inline explicit specialization
             just because the primary template says so.  */
 
-         /* But still keep DECL_DISREGARD_INLINE_LIMITS in sync with
-            the always_inline attribute.  */
-         if (DECL_DISREGARD_INLINE_LIMITS (olddecl)
-             && !DECL_DISREGARD_INLINE_LIMITS (newdecl))
+         if (merge_attr)
            {
-             if (DECL_DECLARED_INLINE_P (newdecl))
-               DECL_DISREGARD_INLINE_LIMITS (newdecl) = true;
-             else
-               DECL_ATTRIBUTES (newdecl)
-                 = remove_attribute ("always_inline",
-                                     DECL_ATTRIBUTES (newdecl));
+             /* But still keep DECL_DISREGARD_INLINE_LIMITS in sync with
+                the always_inline attribute.  */
+             if (DECL_DISREGARD_INLINE_LIMITS (olddecl)
+                 && !DECL_DISREGARD_INLINE_LIMITS (newdecl))
+               {
+                 if (DECL_DECLARED_INLINE_P (newdecl))
+                   DECL_DISREGARD_INLINE_LIMITS (newdecl) = true;
+                 else
+                   DECL_ATTRIBUTES (newdecl)
+                     = remove_attribute ("always_inline",
+                                         DECL_ATTRIBUTES (newdecl));
+               }
+           }
+         else
+           {
+             DECL_DECLARED_INLINE_P (olddecl)
+               = DECL_DECLARED_INLINE_P (newdecl);
+
+             DECL_DISREGARD_INLINE_LIMITS (olddecl)
+               = DECL_DISREGARD_INLINE_LIMITS (newdecl);
+
+             DECL_UNINLINABLE (olddecl) = DECL_UNINLINABLE (newdecl);
            }
        }
       else if (new_defines_function && DECL_INITIAL (olddecl))
@@ -8917,7 +8959,8 @@ grokfndecl (tree ctype,
                                        template_count,
                                        2 * funcdef_flag +
                                        4 * (friendp != 0) +
-                                        8 * concept_p);
+                                       8 * concept_p,
+                                       *attrlist);
   if (decl == error_mark_node)
     return NULL_TREE;