cp: implementation of p1301 for C++
diff mbox series

Message ID CANHA4OhwJPQP=WsnNOt=41wNm5G=+oEnejpgDACwuznbp6tFTw@mail.gmail.com
State New
Headers show
Series
  • cp: implementation of p1301 for C++
Related show

Commit Message

JeanHeyd Meneide July 20, 2019, 5:29 p.m. UTC
Dear GCC Community,

     This patch implements the recently accepted p1301: [[nodiscard("should
have a reason")]]. Aaron Ballman implemented it for Clang in
http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20190715/280158.html
-- this is in preparation for a paper that will soon go to the C Committee
to keep feature-parity with C++ (the C2x draft already has attributes with
syntax exactly like C++).

    Comments welcome: this is my first patch, and probably needs a lot of
help. This is also part of my Google Summer of Code training, to get used
to submitting and sending patches on the gcc-patches list.

Sincerely,
ThePhD

----------------

Comments

Martin Sebor July 20, 2019, 9:48 p.m. UTC | #1
On 7/20/19 11:29 AM, JeanHeyd Meneide wrote:
> Dear GCC Community,
> 
>       This patch implements the recently accepted p1301: [[nodiscard("should
> have a reason")]]. Aaron Ballman implemented it for Clang in
> http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20190715/280158.html
> -- this is in preparation for a paper that will soon go to the C Committee
> to keep feature-parity with C++ (the C2x draft already has attributes with
> syntax exactly like C++).
> 
>      Comments welcome: this is my first patch, and probably needs a lot of
> help. This is also part of my Google Summer of Code training, to get used
> to submitting and sending patches on the gcc-patches list.

Just a few minor notes/suggestions.

...
> --- a/gcc/cp/cvt.c
> +++ b/gcc/cp/cvt.c
> @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "convert.h"
>   #include "stringpool.h"
>   #include "attribs.h"
> +#include "escaped_string.h"
> 
>   static tree convert_to_pointer_force (tree, tree, tsubst_flags_t);
>   static tree build_type_conversion (tree, tree);
> @@ -1029,19 +1030,29 @@ maybe_warn_nodiscard (tree expr, impl_conv_void
> implicit)
>     if (implicit != ICV_CAST && fn
>         && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
>       {
> -      auto_diagnostic_group d;
> +      tree attr = DECL_ATTRIBUTES (fn);
> +      escaped_string msg;
> +      if (attr)
> +         msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
> +      bool has_msg = static_cast<bool>(msg);
> +    auto_diagnostic_group d;
>         if (warning_at (loc, OPT_Wunused_result,
> -      "ignoring return value of %qD, "
> -      "declared with attribute nodiscard", fn))
> - inform (DECL_SOURCE_LOCATION (fn), "declared here");
> +                     "ignoring return value of %qD, "
> +                     "declared with attribute nodiscard%s%s", fn, (has_msg
> ? ": " : ""), (has_msg ? (const char*)msg : "")))
> +        inform (DECL_SOURCE_LOCATION (fn), "declared here");

While making changes here, would you mind adding quotes around
nodiscard (i.e., %<nodiscard%>).  Should the text included in
the attribute also be similarly quoted so that it's highlighted
in the diagnostic?

>       }
>     else if (implicit != ICV_CAST
>      && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
>       {
> +      tree attr = TYPE_ATTRIBUTES (rettype);
> +      escaped_string msg;
> +      if (attr)
> +         msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
> +      bool has_msg = static_cast<bool>(msg);
>         auto_diagnostic_group d;
>         if (warning_at (loc, OPT_Wunused_result,
> -      "ignoring returned value of type %qT, "
> -      "declared with attribute nodiscard", rettype))
> +                      "ignoring returned value of type %qT, "
> +                      "declared with attribute nodiscard%s%s", rettype,

Also here.

> (has_msg ? ": " : ""), (has_msg ? (const char*)msg : "")))
>    {
>     if (fn)
>       inform (DECL_SOURCE_LOCATION (fn),
...
> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> index 37e24a1669c..8c2d056f3eb 100644
> --- a/gcc/cp/tree.c
> +++ b/gcc/cp/tree.c
> @@ -4356,9 +4356,27 @@ zero_init_p (const_tree t)
>      warn_unused_result attribute.  */
> 
>   static tree
> -handle_nodiscard_attribute (tree *node, tree name, tree /*args*/,
> +handle_nodiscard_attribute (tree *node, tree name, tree args,
>       int /*flags*/, bool *no_add_attrs)
>   {
> +  if (!args)
> +    *no_add_attrs = true;
> +  else if (cxx_dialect >= cxx2a)
> +    {
> +      if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
> +        {
> +          error ("nodiscard attribute argument must be a string");

And also here; to reduce the number of format strings that have to
be translated it's best to use:

   error ("%qE attribute argument must be a string constant", name);

> +          *no_add_attrs = true;
> +        }
> +    }
> +  else
> +    {
> +      if (!*no_add_attrs)
> +        {
> +          error("nodiscard attribute does not take any arguments: use flag
> %<-std=c2a%> or better to compile your code");

Also here.  Rather than a colon I think the dominant style is to
use a semicolon but I would suggest:

   error ("%E attribute with an argument only available with "
          "%<-std=c++2a%> or %<-std=gnu++2a%>");

in keeping with other messages like it (see gcc/po/gcc.pot for
examples).  (I assume the option to enable the C++ 2a dialect
is -std=c++2a, not -std=c2a.)

> +++ b/gcc/escaped_string.h
> @@ -0,0 +1,41 @@
...
> +/* A class to handle converting a string that might contain
> +   control characters, (eg newline, form-feed, etc), into one
> +   in which contains escape sequences instead.  */
> +
> +class escaped_string
> +{
> + public:
> +  escaped_string () { m_owned = false; m_str = NULL; };
> +  ~escaped_string () { if (m_owned) free (m_str); }
> +  operator const char *() const { return (const char *) m_str; }
> +  void escape (const char *);
> + private:
> +  char *m_str;
> +  bool  m_owned;
> +};

Since the class isn't safe to copy/assign and it's being moved
to a header, could you please make its copy ctor and assignment
operator private to prevent its objects from accidentally
getting copied and corrupting memory?

...
  +++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
> @@ -0,0 +1,12 @@
> +/* nodiscard attribute tests  */
> +/* { dg-do compile { target c++2a } } */
> +/* { dg-options "-O -ftrack-macro-expansion=0" } */
> +
> +[[nodiscard(123)]] int check1 (void); /* { dg-error "nodiscard attribute
> argument.*must be a string" } */

Using .* might be safe in a test with a single line of output but
not in other tests where it might consume newlines.  It's best to

Martin
Segher Boessenkool July 20, 2019, 10:38 p.m. UTC | #2
On Sat, Jul 20, 2019 at 03:48:40PM -0600, Martin Sebor wrote:
> On 7/20/19 11:29 AM, JeanHeyd Meneide wrote:
> >+[[nodiscard(123)]] int check1 (void); /* { dg-error "nodiscard attribute
> >argument.*must be a string" } */
> 
> Using .* might be safe in a test with a single line of output but
> not in other tests where it might consume newlines.  It's best to

Just start the string with  (?n)  and all is good.


Segher
JeanHeyd Meneide July 21, 2019, 11:23 p.m. UTC | #3
I think I managed to fix all of the issues. Do let me know if I missed
anything!
--------------
diff --git a/.gitignore b/.gitignore
index b53f60db792..8988746a314 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,6 @@ REVISION
 /mpc*
 /gmp*
 /isl*
+
+# ignore some editor-specific files
+.vscode/*
\ No newline at end of file
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 711a31ea597..1c70f9d769f 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -52,6 +52,12 @@
  * gcc/config/or1k/predicates.md (volatile_mem_operand): New.
  (reg_or_mem_operand): New.

+2019-07-22  ThePhD  <phdofthehouse@gmail.com>
+
+ p1301
+ * escaped_string.h: New. Refactored out of tree.c to make more
+ broadly available (e.g. to parser.c, cvt.c).
+
 2019-07-21  Iain Sandoe  <iain@sandoe.co.uk>

  * config/rs6000/rs6000.c (TARGET_NO_PROTOTYPE): Move from here...
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index e6452542bcc..0c3bdbc2fd1 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,9 @@
+2019-07-22  ThePhD  <phdofthehouse@gmail.com>
+
+ p1301
+ * c-family/c-lex.c: increase [[nodiscard]] feature macro
+ value (final value pending post-Cologne mailing)
+
 2019-07-20  Jakub Jelinek  <jakub@redhat.com>

  * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_LOOP.
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 851fd704e5d..f2c0b62c95b 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -353,13 +353,14 @@ c_common_has_attribute (cpp_reader *pfile)
       else if (is_attribute_p ("deprecated", attr_name))
  result = 201309;
       else if (is_attribute_p ("maybe_unused", attr_name)
-       || is_attribute_p ("nodiscard", attr_name)
        || is_attribute_p ("fallthrough", attr_name))
  result = 201603;
       else if (is_attribute_p ("no_unique_address", attr_name)
        || is_attribute_p ("likely", attr_name)
        || is_attribute_p ("unlikely", attr_name))
  result = 201803;
+      else if (is_attribute_p ("nodiscard", attr_name))
+ result = 201907; /* placeholder until C++20 Post-Cologne Working Draft. */
       if (result)
  attr_name = NULL_TREE;
     }
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index d645cdef147..b6aa7f543f6 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,14 @@
+2019-07-22  ThePhD  <phdofthehouse@gmail.com>
+
+ p1301
+ * tree.c: Implement p1301 - nodiscard("should have a reason"))
+ Added C++2a nodiscard string message handling.
+ Increase nodiscard argument handling max_length from 0
+ to 1. (error C++2a gated)
+ * parser.c: add requirement that nodiscard only be seen
+ once in attribute-list (C++2a gated)
+ * cvt.c: add nodiscard message to output, if applicable
+
 2019-07-20  Jason Merrill  <jason@redhat.com>

  * cp-tree.h (ovl_iterator::using_p): A USING_DECL by itself was also
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 23d2aabc483..e473025bb66 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "escaped_string.h"

 static tree convert_to_pointer_force (tree, tree, tsubst_flags_t);
 static tree build_type_conversion (tree, tree);
@@ -1029,26 +1030,42 @@ maybe_warn_nodiscard (tree expr, impl_conv_void
implicit)
   if (implicit != ICV_CAST && fn
       && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
     {
+      tree attr = DECL_ATTRIBUTES (fn);
+      escaped_string msg;
+      if (attr)
+         msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+      bool has_msg = static_cast<bool>(msg);
+      const char* pre_msg = (has_msg ? ": %<" : "");
+      const char* raw_msg = (has_msg ? (const char*)msg : "");
+      const char* post_msg = (has_msg ? "%>" : "");
       auto_diagnostic_group d;
       if (warning_at (loc, OPT_Wunused_result,
-      "ignoring return value of %qD, "
-      "declared with attribute nodiscard", fn))
- inform (DECL_SOURCE_LOCATION (fn), "declared here");
+                     "ignoring return value of %qD, "
+                     "declared with attribute %qE%s%s%s", fn, attr,
pre_msg, raw_msg, post_msg))
+        inform (DECL_SOURCE_LOCATION (fn), "declared here");
     }
   else if (implicit != ICV_CAST
    && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
     {
+      tree attr = TYPE_ATTRIBUTES (rettype);
+      escaped_string msg;
+      if (attr)
+         msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+      bool has_msg = static_cast<bool>(msg);
+      const char* pre_msg = (has_msg ? ": %<" : "");
+      const char* raw_msg = (has_msg ? (const char*)msg : "");
+      const char* post_msg = (has_msg ? "%>" : "");
       auto_diagnostic_group d;
       if (warning_at (loc, OPT_Wunused_result,
-      "ignoring returned value of type %qT, "
-      "declared with attribute nodiscard", rettype))
- {
-  if (fn)
-    inform (DECL_SOURCE_LOCATION (fn),
-    "in call to %qD, declared here", fn);
-  inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
-  "%qT declared here", rettype);
- }
+                      "ignoring returned value of type %qT, "
+                      "declared with attribute %qE%s%s%s", rettype, attr,
pre_msg, raw_msg, post_msg))
+      {
+          if (fn)
+            inform (DECL_SOURCE_LOCATION (fn),
+                 "in call to %qD, declared here", fn);
+          inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
+               "%qT declared here", rettype);
+        }
     }
   else if (TREE_CODE (expr) == TARGET_EXPR
    && lookup_attribute ("warn_unused_result", TYPE_ATTRIBUTES (type)))
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 5c379aaf58d..3ad2f3b8d09 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -26399,13 +26399,26 @@ cp_parser_check_std_attribute (tree attributes,
tree attribute)
     {
       tree name = get_attribute_name (attribute);
       if (is_attribute_p ("noreturn", name)
-  && lookup_attribute ("noreturn", attributes))
- error ("attribute %<noreturn%> can appear at most once "
-       "in an attribute-list");
+     && lookup_attribute ("noreturn", attributes))
+        {
+        error ("attribute %<noreturn%> can appear at most once "
+               "in an attribute-list");
+        }
       else if (is_attribute_p ("deprecated", name)
        && lookup_attribute ("deprecated", attributes))
- error ("attribute %<deprecated%> can appear at most once "
-       "in an attribute-list");
+        {
+         error ("attribute %<deprecated%> can appear at most once "
+                "in an attribute-list");
+        }
+      else if (cxx_dialect >= cxx2a)
+        {
+          if (is_attribute_p ("nodiscard", name)
+         && lookup_attribute ("nodiscard", attributes))
+            {
+            error ("%qE attribute can appear at most once "
+                   "in an attribute-list", name);
+            }
+        }
     }
 }

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 37e24a1669c..f5474be8b03 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4356,12 +4356,31 @@ zero_init_p (const_tree t)
    warn_unused_result attribute.  */

 static tree
-handle_nodiscard_attribute (tree *node, tree name, tree /*args*/,
+handle_nodiscard_attribute (tree *node, tree name, tree args,
     int /*flags*/, bool *no_add_attrs)
 {
+  if (!args)
+    *no_add_attrs = true;
+  else if (cxx_dialect >= cxx2a)
+    {
+      if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+        {
+          error ("%qE attribute argument must be a string constant", name);
+          *no_add_attrs = true;
+        }
+    }
+  else
+    {
+      if (!*no_add_attrs)
+        {
+          error ("%qE attribute with an argument only available with "
+                 "%<-std=c++2a%> or %<-std=gnu++2a%>", name);
+        }
+    }
+
   if (TREE_CODE (*node) == FUNCTION_DECL)
     {
-      if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
+      if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))) &&
DECL_CONSTRUCTOR_P(*node) == 0)
  warning_at (DECL_SOURCE_LOCATION (*node),
     OPT_Wattributes, "%qE attribute applied to %qD with void "
     "return type", name, *node);
@@ -4447,7 +4466,7 @@ const struct attribute_spec std_attribute_table[] =
        affects_type_identity, handler, exclude } */
   { "maybe_unused", 0, 0, false, false, false, false,
     handle_unused_attribute, NULL },
-  { "nodiscard", 0, 0, false, false, false, false,
+  { "nodiscard", 0, 1, false, false, false, false,
     handle_nodiscard_attribute, NULL },
   { "no_unique_address", 0, 0, true, false, false, false,
     handle_no_unique_addr_attribute, NULL },
diff --git a/gcc/escaped_string.h b/gcc/escaped_string.h
new file mode 100644
index 00000000000..ab0fbafc5fd
--- /dev/null
+++ b/gcc/escaped_string.h
@@ -0,0 +1,43 @@
+/* Shared escaped string class.
+   Copyright (C) 1999-2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ESCAPED_STRING_H
+#define GCC_ESCAPED_STRING_H
+
+#include <cstdlib>
+
+/* A class to handle converting a string that might contain
+   control characters, (eg newline, form-feed, etc), into one
+   in which contains escape sequences instead.  */
+
+class escaped_string
+{
+ public:
+  escaped_string () { m_owned = false; m_str = NULL; };
+  ~escaped_string () { if (m_owned) free (m_str); }
+  operator const char *() const { return (const char *) m_str; }
+  void escape (const char *);
+ private:
+  escaped_string(const escaped_string&) {}
+  escaped_string& operator=(const escaped_string&) { return *this; }
+  char *m_str;
+  bool  m_owned;
+};
+
+#endif /* ! GCC_ESCAPED_STRING_H */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 12774c06466..33882a6b968 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -20,6 +20,14 @@
  * gcc.target/or1k/swap-1.c: New test.
  * gcc.target/or1k/swap-2.c: New test.

+2019-07-22  ThePhD  <phdofthehouse@gmail.com>
+
+ p1301 implementation
+ * g++.dg/cpp2a/nodiscard-once.C: New test.
+ * g++.dg/cpp2a/nodiscard-once-clause.C: Ditto.
+ * g++.dg/cpp2a/nodiscard-bad-clause.C: Ditto.
+ * g++.dg/cpp2a/nodiscard-reason.C: Ditto.
+
 2019-07-20  Segher Boessenkool  <segher@kernel.crashing.org>

  * gcc.target/powerpc/volatile-mem.c: New testcase.
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
new file mode 100644
index 00000000000..e2a95ef6625
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard(123)]] int check1 (void); /* { dg-error "(?n)nodiscard.*must
be a string constant" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "(?n)nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
new file mode 100644
index 00000000000..5d7cfafc2c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard("not", "allowed")]] int check1 (void); /* { dg-error
"(?n)wrong number of arguments..*nodiscard" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
new file mode 100644
index 00000000000..d88d5c9d8b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard, nodiscard]] int check1 (void); /* { dg-error
"(?n)nodiscard.*can appear at most once" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
new file mode 100644
index 00000000000..3617365b2b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
@@ -0,0 +1,203 @@
+/* nodiscard attribute tests, adapted from
gcc.dg/attr-warn-unused-result.c.  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+#define NODIS [[nodiscard("exact_message")]]
+#define NODISAI [[nodiscard("exact_inline_message"), gnu::always_inline]]
inline
+enum [[nodiscard("exact_E_message")]] E { e };
+typedef E (*fnt) (void);
+
+typedef struct { long i; } A;
+typedef struct { long i; long j; } B;
+typedef struct { char big[1024]; fnt fn; } C;
+struct [[nodiscard("exact_D_message")]] D { int i; D(); ~D(); };
+
+NODIS E check1 (void);
+NODIS void check2 (void); /* { dg-warning
"(?n)10:.nodiscard.*exact_message" } */
+NODIS int foo; /* { dg-warning "(?n)9:.nodiscard.*exact_message" } */
+int bar (void);
+NODISAI E check3 (void) { return (E)bar (); }
+NODIS A check4 (void);
+NODIS B check5 (void);
+NODIS C check6 (void);
+A bar7 (void);
+B bar8 (void);
+C bar9 (void);
+NODISAI A check7 (void) { return bar7 (); }
+NODISAI B check8 (void) { return bar8 (); }
+NODISAI C check9 (void) { return bar9 (); }
+/* This is useful for checking whether return value of statement
+   expressions (returning int in this case) is used.  */
+NODISAI int check_int_result (int res) { return res; }
+#define GU(v) ({ int e = 0; (v) = bar (); if ((v) < 23) e = 14; e; })
+fnt fnptr;
+NODIS E check10 (void);
+int baz (void);
+NODISAI E check11 (void) { return (E)baz (); }
+int k;
+
+D check12();
+
+void
+test (void)
+{
+  int i = 0, j;
+  const fnt pcheck1 = check1;
+  const fnt pcheck3 = check3;
+  A a;
+  B b;
+  C c;
+  D d;
+  if (check1 ())
+    return;
+  i += check1 ();
+  i += ({ check1 (); });
+  check1 (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check1 ();
+  check1 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  check2 ();
+  (void) check2 ();
+  check2 (), bar ();
+  if (check3 ())
+    return;
+  i += check3 ();
+  i += ({ check3 (); });
+  check3 (); /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+  (void) check3 ();
+  check3 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+  a = check4 ();
+  if (a.i)
+    return;
+  if (check4 ().i)
+    return;
+  if (({ check4 (); }).i)
+    return;
+  check4 (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check4 ();
+  check4 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  b = check5 ();
+  if (b.i + b.j)
+    return;
+  if (check5 ().j)
+    return;
+  if (({ check5 (); }).j)
+    return;
+  check5 (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check5 ();
+  check5 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  c = check6 ();
+  if (c.big[12] + c.big[29])
+    return;
+  if (check6 ().big[27])
+    return;
+  if (({ check6 (); }).big[0])
+    return;
+  check6 (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check6 ();
+  check6 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  a = check7 ();
+  if (a.i)
+    return;
+  if (check7 ().i)
+    return;
+  if (({ check7 (); }).i)
+    return;
+  check7 (); /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+  (void) check7 ();
+  check7 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+  b = check8 ();
+  if (b.i + b.j)
+    return;
+  if (check8 ().j)
+    return;
+  if (({ check8 (); }).j)
+    return;
+  check8 (); /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+  (void) check8 ();
+  check8 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+  c = check9 ();
+  if (c.big[12] + c.big[29])
+    return;
+  if (check9 ().big[27])
+    return;
+  if (({ check9 (); }).big[0])
+    return;
+  check9 (); /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+  (void) check9 ();
+  check9 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+  if (check_int_result (GU (j)))
+    return;
+  i += check_int_result (GU (j));
+  i += ({ check_int_result (GU (j)); });
+  check_int_result (GU (j)); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+  (void) check_int_result (GU (j));
+  check_int_result (GU (j)), bar (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  fnptr = check1;
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  fnptr = check3;
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  if (bar9 ().fn ())
+    return;
+  i += bar9 ().fn ();
+  i += ({ bar9 ().fn (); });
+  bar9 ().fn (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) bar9 ().fn ();
+  bar9 ().fn (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message"
} */
+  if ((k ? check1 : check10) ())
+    return;
+  i += (k ? check1 : check10) ();
+  i += ({ (k ? check1 : check10) (); });
+  (k ? check1 : check10) (); /* { dg-warning
"(?n)nodiscard.*exact_E_message" } */
+  (void) (k ? check1 : check10) ();
+  (k ? check1 : check10) (), bar (); /* { dg-warning
"(?n)nodiscard.*exact_E_message" } */
+  if ((k ? check3 : check11) ())
+    return;
+  i += (k ? check3 : check11) ();
+  i += ({ (k ? check3 : check11) (); });
+  (k ? check3 : check11) (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+  (void) (k ? check3 : check11) ();
+  (k ? check3 : check11) (), bar (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+  if (pcheck1 ())
+    return;
+  i += pcheck1 ();
+  i += ({ pcheck1 (); });
+  pcheck1 (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) pcheck1 ();
+  pcheck1 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  if (pcheck3 ())
+    return;
+  i += pcheck3 ();
+  i += ({ pcheck3 (); });
+  pcheck3 (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) pcheck3 ();
+  pcheck3 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  d = check12 ();
+  if (d.i)
+    return;
+  if (check12 ().i)
+    return;
+  if (({ check12 (); }).i)
+    return;
+  check12 (); /* { dg-warning "(?n)nodiscard.*exact_D_message" } */
+  (void) check12 ();
+  check12 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_D_message" } */
+}
diff --git a/gcc/tree.c b/gcc/tree.c
index 8cf75f22220..362e07d1c33 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "backend.h"
 #include "target.h"
 #include "tree.h"
+#include "escaped_string.h"
 #include "gimple.h"
 #include "tree-pass.h"
 #include "ssa.h"
@@ -13132,22 +13133,6 @@ typedef_variant_p (const_tree type)
   return is_typedef_decl (TYPE_NAME (type));
 }

-/* A class to handle converting a string that might contain
-   control characters, (eg newline, form-feed, etc), into one
-   in which contains escape sequences instead.  */
-
-class escaped_string
-{
- public:
-  escaped_string () { m_owned = false; m_str = NULL; };
-  ~escaped_string () { if (m_owned) free (m_str); }
-  operator const char *() const { return (const char *) m_str; }
-  void escape (const char *);
- private:
-  char *m_str;
-  bool  m_owned;
-};
-
 /* PR 84195: Replace control characters in "unescaped" with their
    escaped equivalents.  Allow newlines if -fmessage-length has
    been set to a non-zero value.  This is done here, rather than
JeanHeyd Meneide July 21, 2019, 11:53 p.m. UTC | #4
Oops. I learned that %< and %> do not get applied as part of the string
arguments, only the initial format string. So, updated patch:
-------------
diff --git a/.gitignore b/.gitignore
index b53f60db792..8988746a314 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,6 @@ REVISION
 /mpc*
 /gmp*
 /isl*
+
+# ignore some editor-specific files
+.vscode/*
\ No newline at end of file
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 711a31ea597..1c70f9d769f 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -52,6 +52,12 @@
  * gcc/config/or1k/predicates.md (volatile_mem_operand): New.
  (reg_or_mem_operand): New.

+2019-07-22  ThePhD  <phdofthehouse@gmail.com>
+
+ p1301
+ * escaped_string.h: New. Refactored out of tree.c to make more
+ broadly available (e.g. to parser.c, cvt.c).
+
 2019-07-21  Iain Sandoe  <iain@sandoe.co.uk>

  * config/rs6000/rs6000.c (TARGET_NO_PROTOTYPE): Move from here...
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index e6452542bcc..0c3bdbc2fd1 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,9 @@
+2019-07-22  ThePhD  <phdofthehouse@gmail.com>
+
+ p1301
+ * c-family/c-lex.c: increase [[nodiscard]] feature macro
+ value (final value pending post-Cologne mailing)
+
 2019-07-20  Jakub Jelinek  <jakub@redhat.com>

  * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_LOOP.
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 851fd704e5d..f2c0b62c95b 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -353,13 +353,14 @@ c_common_has_attribute (cpp_reader *pfile)
       else if (is_attribute_p ("deprecated", attr_name))
  result = 201309;
       else if (is_attribute_p ("maybe_unused", attr_name)
-       || is_attribute_p ("nodiscard", attr_name)
        || is_attribute_p ("fallthrough", attr_name))
  result = 201603;
       else if (is_attribute_p ("no_unique_address", attr_name)
        || is_attribute_p ("likely", attr_name)
        || is_attribute_p ("unlikely", attr_name))
  result = 201803;
+      else if (is_attribute_p ("nodiscard", attr_name))
+ result = 201907; /* placeholder until C++20 Post-Cologne Working Draft. */
       if (result)
  attr_name = NULL_TREE;
     }
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index d645cdef147..b6aa7f543f6 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,14 @@
+2019-07-22  ThePhD  <phdofthehouse@gmail.com>
+
+ p1301
+ * tree.c: Implement p1301 - nodiscard("should have a reason"))
+ Added C++2a nodiscard string message handling.
+ Increase nodiscard argument handling max_length from 0
+ to 1. (error C++2a gated)
+ * parser.c: add requirement that nodiscard only be seen
+ once in attribute-list (C++2a gated)
+ * cvt.c: add nodiscard message to output, if applicable
+
 2019-07-20  Jason Merrill  <jason@redhat.com>

  * cp-tree.h (ovl_iterator::using_p): A USING_DECL by itself was also
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 23d2aabc483..d89b6bcf07a 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "escaped_string.h"

 static tree convert_to_pointer_force (tree, tree, tsubst_flags_t);
 static tree build_type_conversion (tree, tree);
@@ -1029,26 +1030,40 @@ maybe_warn_nodiscard (tree expr, impl_conv_void
implicit)
   if (implicit != ICV_CAST && fn
       && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
     {
+      tree attr = DECL_ATTRIBUTES (fn);
+      escaped_string msg;
+      if (attr)
+         msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+      bool has_msg = static_cast<bool>(msg);
+      const char* pre_msg = (has_msg ? ": " : "");
+      const char* raw_msg = (has_msg ? (const char*)msg : "");
       auto_diagnostic_group d;
       if (warning_at (loc, OPT_Wunused_result,
-      "ignoring return value of %qD, "
-      "declared with attribute nodiscard", fn))
- inform (DECL_SOURCE_LOCATION (fn), "declared here");
+                     "ignoring return value of %qD, "
+                     "declared with attribute %<nodiscard%>%s%s", fn,
pre_msg, raw_msg))
+        inform (DECL_SOURCE_LOCATION (fn), "declared here");
     }
   else if (implicit != ICV_CAST
    && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
     {
+      tree attr = TYPE_ATTRIBUTES (rettype);
+      escaped_string msg;
+      if (attr)
+         msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+      bool has_msg = static_cast<bool>(msg);
+      const char* pre_msg = (has_msg ? ": " : "");
+      const char* raw_msg = (has_msg ? (const char*)msg : "");
       auto_diagnostic_group d;
       if (warning_at (loc, OPT_Wunused_result,
-      "ignoring returned value of type %qT, "
-      "declared with attribute nodiscard", rettype))
- {
-  if (fn)
-    inform (DECL_SOURCE_LOCATION (fn),
-    "in call to %qD, declared here", fn);
-  inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
-  "%qT declared here", rettype);
- }
+                      "ignoring returned value of type %qT, "
+                      "declared with attribute %<nodiscard%>%s%s",
rettype, pre_msg, raw_msg))
+      {
+          if (fn)
+            inform (DECL_SOURCE_LOCATION (fn),
+                 "in call to %qD, declared here", fn);
+          inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
+               "%qT declared here", rettype);
+        }
     }
   else if (TREE_CODE (expr) == TARGET_EXPR
    && lookup_attribute ("warn_unused_result", TYPE_ATTRIBUTES (type)))
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 5c379aaf58d..3ad2f3b8d09 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -26399,13 +26399,26 @@ cp_parser_check_std_attribute (tree attributes,
tree attribute)
     {
       tree name = get_attribute_name (attribute);
       if (is_attribute_p ("noreturn", name)
-  && lookup_attribute ("noreturn", attributes))
- error ("attribute %<noreturn%> can appear at most once "
-       "in an attribute-list");
+     && lookup_attribute ("noreturn", attributes))
+        {
+        error ("attribute %<noreturn%> can appear at most once "
+               "in an attribute-list");
+        }
       else if (is_attribute_p ("deprecated", name)
        && lookup_attribute ("deprecated", attributes))
- error ("attribute %<deprecated%> can appear at most once "
-       "in an attribute-list");
+        {
+         error ("attribute %<deprecated%> can appear at most once "
+                "in an attribute-list");
+        }
+      else if (cxx_dialect >= cxx2a)
+        {
+          if (is_attribute_p ("nodiscard", name)
+         && lookup_attribute ("nodiscard", attributes))
+            {
+            error ("%qE attribute can appear at most once "
+                   "in an attribute-list", name);
+            }
+        }
     }
 }

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 37e24a1669c..f5474be8b03 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4356,12 +4356,31 @@ zero_init_p (const_tree t)
    warn_unused_result attribute.  */

 static tree
-handle_nodiscard_attribute (tree *node, tree name, tree /*args*/,
+handle_nodiscard_attribute (tree *node, tree name, tree args,
     int /*flags*/, bool *no_add_attrs)
 {
+  if (!args)
+    *no_add_attrs = true;
+  else if (cxx_dialect >= cxx2a)
+    {
+      if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+        {
+          error ("%qE attribute argument must be a string constant", name);
+          *no_add_attrs = true;
+        }
+    }
+  else
+    {
+      if (!*no_add_attrs)
+        {
+          error ("%qE attribute with an argument only available with "
+                 "%<-std=c++2a%> or %<-std=gnu++2a%>", name);
+        }
+    }
+
   if (TREE_CODE (*node) == FUNCTION_DECL)
     {
-      if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
+      if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))) &&
DECL_CONSTRUCTOR_P(*node) == 0)
  warning_at (DECL_SOURCE_LOCATION (*node),
     OPT_Wattributes, "%qE attribute applied to %qD with void "
     "return type", name, *node);
@@ -4447,7 +4466,7 @@ const struct attribute_spec std_attribute_table[] =
        affects_type_identity, handler, exclude } */
   { "maybe_unused", 0, 0, false, false, false, false,
     handle_unused_attribute, NULL },
-  { "nodiscard", 0, 0, false, false, false, false,
+  { "nodiscard", 0, 1, false, false, false, false,
     handle_nodiscard_attribute, NULL },
   { "no_unique_address", 0, 0, true, false, false, false,
     handle_no_unique_addr_attribute, NULL },
diff --git a/gcc/escaped_string.h b/gcc/escaped_string.h
new file mode 100644
index 00000000000..ab0fbafc5fd
--- /dev/null
+++ b/gcc/escaped_string.h
@@ -0,0 +1,43 @@
+/* Shared escaped string class.
+   Copyright (C) 1999-2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ESCAPED_STRING_H
+#define GCC_ESCAPED_STRING_H
+
+#include <cstdlib>
+
+/* A class to handle converting a string that might contain
+   control characters, (eg newline, form-feed, etc), into one
+   in which contains escape sequences instead.  */
+
+class escaped_string
+{
+ public:
+  escaped_string () { m_owned = false; m_str = NULL; };
+  ~escaped_string () { if (m_owned) free (m_str); }
+  operator const char *() const { return (const char *) m_str; }
+  void escape (const char *);
+ private:
+  escaped_string(const escaped_string&) {}
+  escaped_string& operator=(const escaped_string&) { return *this; }
+  char *m_str;
+  bool  m_owned;
+};
+
+#endif /* ! GCC_ESCAPED_STRING_H */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 12774c06466..33882a6b968 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -20,6 +20,14 @@
  * gcc.target/or1k/swap-1.c: New test.
  * gcc.target/or1k/swap-2.c: New test.

+2019-07-22  ThePhD  <phdofthehouse@gmail.com>
+
+ p1301 implementation
+ * g++.dg/cpp2a/nodiscard-once.C: New test.
+ * g++.dg/cpp2a/nodiscard-once-clause.C: Ditto.
+ * g++.dg/cpp2a/nodiscard-bad-clause.C: Ditto.
+ * g++.dg/cpp2a/nodiscard-reason.C: Ditto.
+
 2019-07-20  Segher Boessenkool  <segher@kernel.crashing.org>

  * gcc.target/powerpc/volatile-mem.c: New testcase.
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
new file mode 100644
index 00000000000..e2a95ef6625
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard(123)]] int check1 (void); /* { dg-error "(?n)nodiscard.*must
be a string constant" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "(?n)nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
new file mode 100644
index 00000000000..5d7cfafc2c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard("not", "allowed")]] int check1 (void); /* { dg-error
"(?n)wrong number of arguments..*nodiscard" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
new file mode 100644
index 00000000000..d88d5c9d8b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard, nodiscard]] int check1 (void); /* { dg-error
"(?n)nodiscard.*can appear at most once" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
new file mode 100644
index 00000000000..3617365b2b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
@@ -0,0 +1,203 @@
+/* nodiscard attribute tests, adapted from
gcc.dg/attr-warn-unused-result.c.  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+#define NODIS [[nodiscard("exact_message")]]
+#define NODISAI [[nodiscard("exact_inline_message"), gnu::always_inline]]
inline
+enum [[nodiscard("exact_E_message")]] E { e };
+typedef E (*fnt) (void);
+
+typedef struct { long i; } A;
+typedef struct { long i; long j; } B;
+typedef struct { char big[1024]; fnt fn; } C;
+struct [[nodiscard("exact_D_message")]] D { int i; D(); ~D(); };
+
+NODIS E check1 (void);
+NODIS void check2 (void); /* { dg-warning
"(?n)10:.nodiscard.*exact_message" } */
+NODIS int foo; /* { dg-warning "(?n)9:.nodiscard.*exact_message" } */
+int bar (void);
+NODISAI E check3 (void) { return (E)bar (); }
+NODIS A check4 (void);
+NODIS B check5 (void);
+NODIS C check6 (void);
+A bar7 (void);
+B bar8 (void);
+C bar9 (void);
+NODISAI A check7 (void) { return bar7 (); }
+NODISAI B check8 (void) { return bar8 (); }
+NODISAI C check9 (void) { return bar9 (); }
+/* This is useful for checking whether return value of statement
+   expressions (returning int in this case) is used.  */
+NODISAI int check_int_result (int res) { return res; }
+#define GU(v) ({ int e = 0; (v) = bar (); if ((v) < 23) e = 14; e; })
+fnt fnptr;
+NODIS E check10 (void);
+int baz (void);
+NODISAI E check11 (void) { return (E)baz (); }
+int k;
+
+D check12();
+
+void
+test (void)
+{
+  int i = 0, j;
+  const fnt pcheck1 = check1;
+  const fnt pcheck3 = check3;
+  A a;
+  B b;
+  C c;
+  D d;
+  if (check1 ())
+    return;
+  i += check1 ();
+  i += ({ check1 (); });
+  check1 (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check1 ();
+  check1 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  check2 ();
+  (void) check2 ();
+  check2 (), bar ();
+  if (check3 ())
+    return;
+  i += check3 ();
+  i += ({ check3 (); });
+  check3 (); /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+  (void) check3 ();
+  check3 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+  a = check4 ();
+  if (a.i)
+    return;
+  if (check4 ().i)
+    return;
+  if (({ check4 (); }).i)
+    return;
+  check4 (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check4 ();
+  check4 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  b = check5 ();
+  if (b.i + b.j)
+    return;
+  if (check5 ().j)
+    return;
+  if (({ check5 (); }).j)
+    return;
+  check5 (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check5 ();
+  check5 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  c = check6 ();
+  if (c.big[12] + c.big[29])
+    return;
+  if (check6 ().big[27])
+    return;
+  if (({ check6 (); }).big[0])
+    return;
+  check6 (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check6 ();
+  check6 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  a = check7 ();
+  if (a.i)
+    return;
+  if (check7 ().i)
+    return;
+  if (({ check7 (); }).i)
+    return;
+  check7 (); /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+  (void) check7 ();
+  check7 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+  b = check8 ();
+  if (b.i + b.j)
+    return;
+  if (check8 ().j)
+    return;
+  if (({ check8 (); }).j)
+    return;
+  check8 (); /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+  (void) check8 ();
+  check8 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+  c = check9 ();
+  if (c.big[12] + c.big[29])
+    return;
+  if (check9 ().big[27])
+    return;
+  if (({ check9 (); }).big[0])
+    return;
+  check9 (); /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+  (void) check9 ();
+  check9 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+  if (check_int_result (GU (j)))
+    return;
+  i += check_int_result (GU (j));
+  i += ({ check_int_result (GU (j)); });
+  check_int_result (GU (j)); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+  (void) check_int_result (GU (j));
+  check_int_result (GU (j)), bar (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  fnptr = check1;
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  fnptr = check3;
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  if (bar9 ().fn ())
+    return;
+  i += bar9 ().fn ();
+  i += ({ bar9 ().fn (); });
+  bar9 ().fn (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) bar9 ().fn ();
+  bar9 ().fn (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message"
} */
+  if ((k ? check1 : check10) ())
+    return;
+  i += (k ? check1 : check10) ();
+  i += ({ (k ? check1 : check10) (); });
+  (k ? check1 : check10) (); /* { dg-warning
"(?n)nodiscard.*exact_E_message" } */
+  (void) (k ? check1 : check10) ();
+  (k ? check1 : check10) (), bar (); /* { dg-warning
"(?n)nodiscard.*exact_E_message" } */
+  if ((k ? check3 : check11) ())
+    return;
+  i += (k ? check3 : check11) ();
+  i += ({ (k ? check3 : check11) (); });
+  (k ? check3 : check11) (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+  (void) (k ? check3 : check11) ();
+  (k ? check3 : check11) (), bar (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+  if (pcheck1 ())
+    return;
+  i += pcheck1 ();
+  i += ({ pcheck1 (); });
+  pcheck1 (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) pcheck1 ();
+  pcheck1 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  if (pcheck3 ())
+    return;
+  i += pcheck3 ();
+  i += ({ pcheck3 (); });
+  pcheck3 (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) pcheck3 ();
+  pcheck3 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  d = check12 ();
+  if (d.i)
+    return;
+  if (check12 ().i)
+    return;
+  if (({ check12 (); }).i)
+    return;
+  check12 (); /* { dg-warning "(?n)nodiscard.*exact_D_message" } */
+  (void) check12 ();
+  check12 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_D_message" } */
+}
diff --git a/gcc/tree.c b/gcc/tree.c
index 8cf75f22220..362e07d1c33 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "backend.h"
 #include "target.h"
 #include "tree.h"
+#include "escaped_string.h"
 #include "gimple.h"
 #include "tree-pass.h"
 #include "ssa.h"
@@ -13132,22 +13133,6 @@ typedef_variant_p (const_tree type)
   return is_typedef_decl (TYPE_NAME (type));
 }

-/* A class to handle converting a string that might contain
-   control characters, (eg newline, form-feed, etc), into one
-   in which contains escape sequences instead.  */
-
-class escaped_string
-{
- public:
-  escaped_string () { m_owned = false; m_str = NULL; };
-  ~escaped_string () { if (m_owned) free (m_str); }
-  operator const char *() const { return (const char *) m_str; }
-  void escape (const char *);
- private:
-  char *m_str;
-  bool  m_owned;
-};
-
 /* PR 84195: Replace control characters in "unescaped" with their
    escaped equivalents.  Allow newlines if -fmessage-length has
    been set to a non-zero value.  This is done here, rather than
Segher Boessenkool July 22, 2019, 6:13 a.m. UTC | #5
Hi JeanHeyd,

Just some patch-technical comments:

On Mon, Jul 22, 2019 at 01:53:23AM +0200, JeanHeyd Meneide wrote:
> diff --git a/.gitignore b/.gitignore
> index b53f60db792..8988746a314 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -55,3 +55,6 @@ REVISION
>  /mpc*
>  /gmp*
>  /isl*
> +
> +# ignore some editor-specific files
> +.vscode/*

The dotfiles are earlier in this file.

Do we want this here at all?  A user of this IDE should probably have
something like this in his global .gitignore, instead.

(Oh, and it should be a separate patch, anyway).

> \ No newline at end of file

That is wrong; please end all (text) files with a newline.

> diff --git a/gcc/ChangeLog b/gcc/ChangeLog

Don't put changelogs in the diff; instead, put them *before* the patch.

> index 711a31ea597..1c70f9d769f 100644
> --- a/gcc/ChangeLog
> +++ b/gcc/ChangeLog
> @@ -52,6 +52,12 @@
>   * gcc/config/or1k/predicates.md (volatile_mem_operand): New.
>   (reg_or_mem_operand): New.
> 
> +2019-07-22  ThePhD  <phdofthehouse@gmail.com>
> +
> + p1301
> + * escaped_string.h: New. Refactored out of tree.c to make more
> + broadly available (e.g. to parser.c, cvt.c).
> +
>  2019-07-21  Iain Sandoe  <iain@sandoe.co.uk>
> 
>   * config/rs6000/rs6000.c (TARGET_NO_PROTOTYPE): Move from here...

No one else but you can ever apply that like this.  Also, you're adding
and entry to the middle of a changelog?

> diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
> index e6452542bcc..0c3bdbc2fd1 100644
> --- a/gcc/c-family/ChangeLog
> +++ b/gcc/c-family/ChangeLog
> @@ -1,3 +1,9 @@
> +2019-07-22  ThePhD  <phdofthehouse@gmail.com>
> +
> + p1301
> + * c-family/c-lex.c: increase [[nodiscard]] feature macro
> + value (final value pending post-Cologne mailing)

Sentences end in a full stop.  Sentences start with a capital letter.
All lines in a changelog are indented with a tab (not with a space).

> -      "ignoring return value of %qD, "
> -      "declared with attribute nodiscard", fn))
> - inform (DECL_SOURCE_LOCATION (fn), "declared here");
> +                     "ignoring return value of %qD, "
> +                     "declared with attribute %<nodiscard%>%s%s", fn,
> pre_msg, raw_msg))
> +        inform (DECL_SOURCE_LOCATION (fn), "declared here");

Your email client wraps lines.  This line is much too long, too.

Your email client ate those tabs as well it seems?  Please fix that.


Segher
JeanHeyd Meneide July 25, 2019, 11:47 a.m. UTC | #6
I think I got the tabs right...? You would not believe how unbelievably
hard it is, just to mail a diff!

-----

07-24-2019    ThePhD    <phdofthehouse@gmail.com>

gcc/

* escaped_string.h: New. Refactored out of tree.c to make more
broadly available (e.g. to parser.c, cvt.c).
* tree.c: remove escaped_string class

gcc/c-family

* c-lex.c - update attribute value

gcc/cp/

* tree.c: Implement p1301 - nodiscard("should have a reason"))
Added C++2a nodiscard string message handling.
Increase nodiscard argument handling max_length from 0
to 1. (error C++2a gated)
* parser.c: add requirement that nodiscard only be seen
once in attribute-list (C++2a gated)
* cvt.c: add nodiscard message to output, if applicable

-----

diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 851fd704e5d..f5870d095f2 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -345,24 +345,26 @@ c_common_has_attribute (cpp_reader *pfile)
    attr_name = NULL_TREE;
   }
  }
-  else
-  {
-  /* Some standard attributes need special handling. */
-  if (is_attribute_p ("noreturn", attr_name))
-   result = 200809;
-  else if (is_attribute_p ("deprecated", attr_name))
-   result = 201309;
-  else if (is_attribute_p ("maybe_unused", attr_name)
-    || is_attribute_p ("nodiscard", attr_name)
-    || is_attribute_p ("fallthrough", attr_name))
-   result = 201603;
-  else if (is_attribute_p ("no_unique_address", attr_name)
-    || is_attribute_p ("likely", attr_name)
-    || is_attribute_p ("unlikely", attr_name))
-   result = 201803;
-  if (result)
-   attr_name = NULL_TREE;
-  }
+   else
+     {
+       /* Some standard attributes need special handling. */
+       if (is_attribute_p ("noreturn", attr_name))
+         result = 200809;
+       else if (is_attribute_p ("deprecated", attr_name))
+         result = 201309;
+       else if (is_attribute_p ("maybe_unused", attr_name)
+        || is_attribute_p ("fallthrough", attr_name))
+         result = 201603;
+       else if (is_attribute_p ("no_unique_address", attr_name)
+        || is_attribute_p ("likely", attr_name)
+        || is_attribute_p ("unlikely", attr_name))
+         result = 201803;
+       else if (is_attribute_p ("nodiscard", attr_name))
+         result = 201907; /* placeholder until C++20 Post-Cologne Working
Draft. */
+
+       if (result)
+         attr_name = NULL_TREE;
+     }
 }
if (attr_name)
 {
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 23d2aabc483..4a5128c76a1 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "convert.h"
#include "stringpool.h"
#include "attribs.h"
+#include "escaped_string.h"
static tree convert_to_pointer_force (tree, tree, tsubst_flags_t);
static tree build_type_conversion (tree, tree);
@@ -1026,49 +1027,99 @@ maybe_warn_nodiscard (tree expr, impl_conv_void
implicit)
tree rettype = TREE_TYPE (type);
tree fn = cp_get_fndecl_from_callee (callee);
- if (implicit != ICV_CAST && fn
- && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
- {
- auto_diagnostic_group d;
- if (warning_at (loc, OPT_Wunused_result,
-    "ignoring return value of %qD, "
-    "declared with attribute nodiscard", fn))
- inform (DECL_SOURCE_LOCATION (fn), "declared here");
- }
- else if (implicit != ICV_CAST
-  && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
- {
- auto_diagnostic_group d;
- if (warning_at (loc, OPT_Wunused_result,
-    "ignoring returned value of type %qT, "
-    "declared with attribute nodiscard", rettype))
- {
-  if (fn)
-  inform (DECL_SOURCE_LOCATION (fn),
-    "in call to %qD, declared here", fn);
-  inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
-    "%qT declared here", rettype);
- }
- }
- else if (TREE_CODE (expr) == TARGET_EXPR
-  && lookup_attribute ("warn_unused_result", TYPE_ATTRIBUTES (type)))
- {
- /* The TARGET_EXPR confuses do_warn_unused_result into thinking that the
-  result is used, so handle that case here. */
- if (fn)
- {
-  auto_diagnostic_group d;
-  if (warning_at (loc, OPT_Wunused_result,
-      "ignoring return value of %qD, "
-      "declared with attribute %<warn_unused_result%>",
-      fn))
-  inform (DECL_SOURCE_LOCATION (fn), "declared here");
- }
- else
- warning_at (loc, OPT_Wunused_result,
-    "ignoring return value of function "
-    "declared with attribute %<warn_unused_result%>");
- }
+ if (implicit != ICV_CAST && fn
+   && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
+   {
+     tree attr = DECL_ATTRIBUTES (fn);
+     escaped_string msg;
+     if (attr)
+       msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+     bool has_msg = static_cast<bool>(msg);
+     const char* format = (has_msg ?
+       "ignoring return value of %qD, "
+       "declared with attribute %<nodiscard%>: %<%s%>" :
+       "ignoring return value of %qD, "
+       "declared with attribute %<nodiscard%>%s");
+     const char* raw_msg = (has_msg ? (const char*)msg : "");
+     auto_diagnostic_group d;
+     if (warning_at (loc, OPT_Wunused_result,
+      format, fn, raw_msg))
+       {
+         inform (DECL_SOURCE_LOCATION (fn), "declared here");
+       }
+   }
+ else if (implicit != ICV_CAST
+  && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
+   {
+     tree attr = TYPE_ATTRIBUTES (rettype);
+     escaped_string msg;
+     if (attr)
+       msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+     bool has_msg = static_cast<bool>(msg);
+     const char* format = (has_msg ?
+       "ignoring returned value of type %qT, "
+       "declared with attribute %<nodiscard%>: %<%s%>" :
+       "ignoring return value of type %qT, "
+       "declared with attribute %<nodiscard%>%s");
+     const char* raw_msg = (has_msg ? (const char*)msg : "");
+     auto_diagnostic_group d;
+     if (warning_at (loc, OPT_Wunused_result,
+      format, rettype, raw_msg))
+       {
+         if (fn)
+           {
+             inform (DECL_SOURCE_LOCATION (fn),
+              "in call to %qD, declared here", fn);
+           }
+         inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
+          "%qT declared here", rettype);
+     }
+   }
+ else if (TREE_CODE (expr) == TARGET_EXPR)
+   {
+     if (lookup_attribute ("warn_unused_result", TYPE_ATTRIBUTES (type)))
+       {
+         /* The TARGET_EXPR confuses do_warn_unused_result into thinking
that the
+         result is used, so handle that case here. */
+         if (fn)
+           {
+             auto_diagnostic_group d;
+             if (warning_at (loc, OPT_Wunused_result,
+              "ignoring return value of %qD, "
+              "declared with attribute %<warn_unused_result%>",
+              fn))
+               {
+                 inform (DECL_SOURCE_LOCATION (fn),
+                         "declared here");
+               }
+           }
+         else
+           warning_at (loc, OPT_Wunused_result,
+             "ignoring return value of function "
+             "declared with attribute %<warn_unused_result%>");
+       }
+     else if (implicit != ICV_CAST && fn
+       && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
+       {
+         tree attr = DECL_ATTRIBUTES (fn);
+         escaped_string msg;
+         if (attr)
+           msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE
(attr))));
+         bool has_msg = static_cast<bool>(msg);
+         const char* format = (has_msg ?
+           "ignoring return value of %qD, "
+           "declared with attribute %<nodiscard%>: %<%s%>" :
+           "ignoring return value of %qD, "
+           "declared with attribute %<nodiscard%>%s");
+         const char* raw_msg = (has_msg ? (const char*)msg : "");
+         auto_diagnostic_group d;
+         if (warning_at (loc, OPT_Wunused_result,
+          format, fn, raw_msg))
+           {
+             inform (DECL_SOURCE_LOCATION (fn), "declared here");
+           }
+       }
+   }
}
/* When an expression is used in a void context, its value is discarded and
@@ -1174,17 +1225,17 @@ convert_to_void (tree expr, impl_conv_void
implicit, tsubst_flags_t complain)
case CALL_EXPR: /* We have a special meaning for volatile void fn(). */
/* cdtors may return this or void, depending on
-  targetm.cxx.cdtor_returns_this, but this shouldn't affect our
-  decisions here: neither nodiscard warnings (nodiscard cdtors
-  are nonsensical), nor should any constexpr or template
-  instantiations be affected by an ABI property that is, or at
-  least ought to be transparent to the language. */
- if (tree fn = cp_get_callee_fndecl_nofold (expr))
- if (DECL_CONSTRUCTOR_P (fn) || DECL_DESTRUCTOR_P (fn))
-  return expr;
-
- maybe_warn_nodiscard (expr, implicit);
- break;
+     targetm.cxx.cdtor_returns_this, but this shouldn't affect our
+     decisions here: neither nodiscard warnings (nodiscard cdtors
+     are nonsensical), nor should any constexpr or template
+     instantiations be affected by an ABI property that is, or at
+     least ought to be transparent to the language. */
+     if (tree fn = cp_get_callee_fndecl_nofold (expr))
+       if (DECL_DESTRUCTOR_P (fn))
+         return expr;
+
+     maybe_warn_nodiscard (expr, implicit);
+     break;
case INDIRECT_REF:
{
@@ -1396,28 +1447,29 @@ convert_to_void (tree expr, impl_conv_void
implicit, tsubst_flags_t complain)
}
case TARGET_EXPR:
- /* Don't bother with the temporary object returned from a function if
-  we don't use it, don't need to destroy it, and won't abort in
-  assign_temp. We'll still
-  allocate space for it in expand_call or declare_return_variable,
-  but we don't need to track it through all the tree phases. */
- if (TARGET_EXPR_IMPLICIT_P (expr)
-  && !TREE_ADDRESSABLE (TREE_TYPE (expr)))
- {
-  tree init = TARGET_EXPR_INITIAL (expr);
-  if (TREE_CODE (init) == AGGR_INIT_EXPR
-  && !AGGR_INIT_VIA_CTOR_P (init))
-  {
-  tree fn = AGGR_INIT_EXPR_FN (init);
-  expr = build_call_array_loc (input_location,
-          TREE_TYPE (TREE_TYPE
-            (TREE_TYPE (fn))),
-          fn,
-          aggr_init_expr_nargs (init),
-          AGGR_INIT_EXPR_ARGP (init));
-  }
- }
- maybe_warn_nodiscard (expr, implicit);
+     /* Don't bother with the temporary object returned from a function if
+     we don't use it, don't need to destroy it, and won't abort in
+     assign_temp. We'll still
+     allocate space for it in expand_call or declare_return_variable,
+     but we don't need to track it through all the tree phases. */
+     if (TARGET_EXPR_IMPLICIT_P (expr)
+      && !TREE_ADDRESSABLE (TREE_TYPE (expr)))
+       {
+         tree init = TARGET_EXPR_INITIAL (expr);
+         if (TREE_CODE (init) == AGGR_INIT_EXPR
+          && !AGGR_INIT_VIA_CTOR_P (init))
+           {
+             tree fn = AGGR_INIT_EXPR_FN (init);
+             expr = build_call_array_loc (input_location,
+             TREE_TYPE (TREE_TYPE
+             (TREE_TYPE (fn))),
+             fn,
+             aggr_init_expr_nargs (init),
+             AGGR_INIT_EXPR_ARGP (init));
+           }
+       }
+
+     maybe_warn_nodiscard (expr, implicit);
break;
default:;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index ebeffdb775f..c04afa47d71 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -26399,13 +26399,26 @@ cp_parser_check_std_attribute (tree attributes,
tree attribute)
{
tree name = get_attribute_name (attribute);
if (is_attribute_p ("noreturn", name)
-  && lookup_attribute ("noreturn", attributes))
- error ("attribute %<noreturn%> can appear at most once "
-  "in an attribute-list");
+  && lookup_attribute ("noreturn", attributes))
+ {
+  error ("attribute %<noreturn%> can appear at most once "
+  "in an attribute-list");
+ }
else if (is_attribute_p ("deprecated", name)
  && lookup_attribute ("deprecated", attributes))
- error ("attribute %<deprecated%> can appear at most once "
-  "in an attribute-list");
+ {
+  error ("attribute %<deprecated%> can appear at most once "
+  "in an attribute-list");
+ }
+ else if (cxx_dialect >= cxx2a)
+ {
+ if (is_attribute_p ("nodiscard", name)
+  && lookup_attribute ("nodiscard", attributes))
+ {
+  error ("%qE attribute can appear at most once "
+  "in an attribute-list", name);
+ }
+ }
}
}
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 37e24a1669c..f5474be8b03 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4356,12 +4356,31 @@ zero_init_p (const_tree t)
warn_unused_result attribute. */
static tree
-handle_nodiscard_attribute (tree *node, tree name, tree /*args*/,
+handle_nodiscard_attribute (tree *node, tree name, tree args,
      int /*flags*/, bool *no_add_attrs)
{
+ if (!args)
+ *no_add_attrs = true;
+ else if (cxx_dialect >= cxx2a)
+ {
+ if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ error ("%qE attribute argument must be a string constant", name);
+ *no_add_attrs = true;
+ }
+ }
+ else
+ {
+ if (!*no_add_attrs)
+ {
+ error ("%qE attribute with an argument only available with "
+ "%<-std=c++2a%> or %<-std=gnu++2a%>", name);
+ }
+ }
+
if (TREE_CODE (*node) == FUNCTION_DECL)
{
- if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
+ if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))) &&
DECL_CONSTRUCTOR_P(*node) == 0)
 warning_at (DECL_SOURCE_LOCATION (*node),
    OPT_Wattributes, "%qE attribute applied to %qD with void "
    "return type", name, *node);
@@ -4447,7 +4466,7 @@ const struct attribute_spec std_attribute_table[] =
affects_type_identity, handler, exclude } */
{ "maybe_unused", 0, 0, false, false, false, false,
handle_unused_attribute, NULL },
- { "nodiscard", 0, 0, false, false, false, false,
+ { "nodiscard", 0, 1, false, false, false, false,
handle_nodiscard_attribute, NULL },
{ "no_unique_address", 0, 0, true, false, false, false,
handle_no_unique_addr_attribute, NULL },
diff --git a/gcc/escaped_string.h b/gcc/escaped_string.h
new file mode 100644
index 00000000000..ab0fbafc5fd
--- /dev/null
+++ b/gcc/escaped_string.h
@@ -0,0 +1,43 @@
+/* Shared escaped string class.
+ Copyright (C) 1999-2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_ESCAPED_STRING_H
+#define GCC_ESCAPED_STRING_H
+
+#include <cstdlib>
+
+/* A class to handle converting a string that might contain
+ control characters, (eg newline, form-feed, etc), into one
+ in which contains escape sequences instead. */
+
+class escaped_string
+{
+ public:
+ escaped_string () { m_owned = false; m_str = NULL; };
+ ~escaped_string () { if (m_owned) free (m_str); }
+ operator const char *() const { return (const char *) m_str; }
+ void escape (const char *);
+ private:
+ escaped_string(const escaped_string&) {}
+ escaped_string& operator=(const escaped_string&) { return *this; }
+ char *m_str;
+ bool m_owned;
+};
+
+#endif /* ! GCC_ESCAPED_STRING_H */
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
new file mode 100644
index 00000000000..e2a95ef6625
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard(123)]] int check1 (void); /* { dg-error "(?n)nodiscard.*must
be a string constant" } */
+
+void
+test (void)
+{
+ check1 ();   /* { dg-warning "(?n)nodiscard" } */
+ (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
new file mode 100644
index 00000000000..5d7cfafc2c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard("not", "allowed")]] int check1 (void); /* { dg-error
"(?n)wrong number of arguments..*nodiscard" } */
+
+void
+test (void)
+{
+ check1 ();   /* { dg-warning "nodiscard" } */
+ (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
new file mode 100644
index 00000000000..d88d5c9d8b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard, nodiscard]] int check1 (void); /* { dg-error
"(?n)nodiscard.*can appear at most once" } */
+
+void
+test (void)
+{
+ check1 ();   /* { dg-warning "nodiscard" } */
+ (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
new file mode 100644
index 00000000000..3617365b2b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
@@ -0,0 +1,203 @@
+/* nodiscard attribute tests, adapted from
gcc.dg/attr-warn-unused-result.c. */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+#define NODIS [[nodiscard("exact_message")]]
+#define NODISAI [[nodiscard("exact_inline_message"), gnu::always_inline]]
inline
+enum [[nodiscard("exact_E_message")]] E { e };
+typedef E (*fnt) (void);
+
+typedef struct { long i; } A;
+typedef struct { long i; long j; } B;
+typedef struct { char big[1024]; fnt fn; } C;
+struct [[nodiscard("exact_D_message")]] D { int i; D(); ~D(); };
+
+NODIS E check1 (void);
+NODIS void check2 (void); /* { dg-warning
"(?n)10:.nodiscard.*exact_message" } */
+NODIS int foo;   /* { dg-warning "(?n)9:.nodiscard.*exact_message" } */
+int bar (void);
+NODISAI E check3 (void) { return (E)bar (); }
+NODIS A check4 (void);
+NODIS B check5 (void);
+NODIS C check6 (void);
+A bar7 (void);
+B bar8 (void);
+C bar9 (void);
+NODISAI A check7 (void) { return bar7 (); }
+NODISAI B check8 (void) { return bar8 (); }
+NODISAI C check9 (void) { return bar9 (); }
+/* This is useful for checking whether return value of statement
+ expressions (returning int in this case) is used. */
+NODISAI int check_int_result (int res) { return res; }
+#define GU(v) ({ int e = 0; (v) = bar (); if ((v) < 23) e = 14; e; })
+fnt fnptr;
+NODIS E check10 (void);
+int baz (void);
+NODISAI E check11 (void) { return (E)baz (); }
+int k;
+
+D check12();
+
+void
+test (void)
+{
+ int i = 0, j;
+ const fnt pcheck1 = check1;
+ const fnt pcheck3 = check3;
+ A a;
+ B b;
+ C c;
+ D d;
+ if (check1 ())
+ return;
+ i += check1 ();
+ i += ({ check1 (); });
+ check1 ();   /* { dg-warning "(?n)nodiscard.*exact_message" } */
+ (void) check1 ();
+ check1 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+ check2 ();
+ (void) check2 ();
+ check2 (), bar ();
+ if (check3 ())
+ return;
+ i += check3 ();
+ i += ({ check3 (); });
+ check3 ();   /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+ (void) check3 ();
+ check3 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+ a = check4 ();
+ if (a.i)
+ return;
+ if (check4 ().i)
+ return;
+ if (({ check4 (); }).i)
+ return;
+ check4 ();   /* { dg-warning "(?n)nodiscard.*exact_message" } */
+ (void) check4 ();
+ check4 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+ b = check5 ();
+ if (b.i + b.j)
+ return;
+ if (check5 ().j)
+ return;
+ if (({ check5 (); }).j)
+ return;
+ check5 ();   /* { dg-warning "(?n)nodiscard.*exact_message" } */
+ (void) check5 ();
+ check5 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+ c = check6 ();
+ if (c.big[12] + c.big[29])
+ return;
+ if (check6 ().big[27])
+ return;
+ if (({ check6 (); }).big[0])
+ return;
+ check6 ();   /* { dg-warning "(?n)nodiscard.*exact_message" } */
+ (void) check6 ();
+ check6 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_message" } */
+ a = check7 ();
+ if (a.i)
+ return;
+ if (check7 ().i)
+ return;
+ if (({ check7 (); }).i)
+ return;
+ check7 ();   /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+ (void) check7 ();
+ check7 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+ b = check8 ();
+ if (b.i + b.j)
+ return;
+ if (check8 ().j)
+ return;
+ if (({ check8 (); }).j)
+ return;
+ check8 ();   /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+ (void) check8 ();
+ check8 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+ c = check9 ();
+ if (c.big[12] + c.big[29])
+ return;
+ if (check9 ().big[27])
+ return;
+ if (({ check9 (); }).big[0])
+ return;
+ check9 ();   /* { dg-warning "(?n)nodiscard.*exact_inline_message" } */
+ (void) check9 ();
+ check9 (), bar (); /* { dg-warning "(?n)nodiscard.*exact_inline_message"
} */
+ if (check_int_result (GU (j)))
+ return;
+ i += check_int_result (GU (j));
+ i += ({ check_int_result (GU (j)); });
+ check_int_result (GU (j)); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+ (void) check_int_result (GU (j));
+ check_int_result (GU (j)), bar (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+ if (fnptr ())
+ return;
+ i += fnptr ();
+ i += ({ fnptr (); });
+ fnptr ();    /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ (void) fnptr ();
+ fnptr (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ fnptr = check1;
+ if (fnptr ())
+ return;
+ i += fnptr ();
+ i += ({ fnptr (); });
+ fnptr ();    /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ (void) fnptr ();
+ fnptr (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ fnptr = check3;
+ if (fnptr ())
+ return;
+ i += fnptr ();
+ i += ({ fnptr (); });
+ fnptr ();    /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ (void) fnptr ();
+ fnptr (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ if (bar9 ().fn ())
+ return;
+ i += bar9 ().fn ();
+ i += ({ bar9 ().fn (); });
+ bar9 ().fn (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ (void) bar9 ().fn ();
+ bar9 ().fn (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" }
*/
+ if ((k ? check1 : check10) ())
+ return;
+ i += (k ? check1 : check10) ();
+ i += ({ (k ? check1 : check10) (); });
+ (k ? check1 : check10) (); /* { dg-warning
"(?n)nodiscard.*exact_E_message" } */
+ (void) (k ? check1 : check10) ();
+ (k ? check1 : check10) (), bar (); /* { dg-warning
"(?n)nodiscard.*exact_E_message" } */
+ if ((k ? check3 : check11) ())
+ return;
+ i += (k ? check3 : check11) ();
+ i += ({ (k ? check3 : check11) (); });
+ (k ? check3 : check11) (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+ (void) (k ? check3 : check11) ();
+ (k ? check3 : check11) (), bar (); /* { dg-warning
"(?n)nodiscard.*exact_inline_message" } */
+ if (pcheck1 ())
+ return;
+ i += pcheck1 ();
+ i += ({ pcheck1 (); });
+ pcheck1 ();    /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ (void) pcheck1 ();
+ pcheck1 (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ if (pcheck3 ())
+ return;
+ i += pcheck3 ();
+ i += ({ pcheck3 (); });
+ pcheck3 ();    /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ (void) pcheck3 ();
+ pcheck3 (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+ d = check12 ();
+ if (d.i)
+ return;
+ if (check12 ().i)
+ return;
+ if (({ check12 (); }).i)
+ return;
+ check12 ();    /* { dg-warning "(?n)nodiscard.*exact_D_message" } */
+ (void) check12 ();
+ check12 (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_D_message" } */
+}
diff --git a/gcc/tree.c b/gcc/tree.c
index 8cf75f22220..362e07d1c33 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see
#include "backend.h"
#include "target.h"
#include "tree.h"
+#include "escaped_string.h"
#include "gimple.h"
#include "tree-pass.h"
#include "ssa.h"
@@ -13132,22 +13133,6 @@ typedef_variant_p (const_tree type)
return is_typedef_decl (TYPE_NAME (type));
}
-/* A class to handle converting a string that might contain
- control characters, (eg newline, form-feed, etc), into one
- in which contains escape sequences instead. */
-
-class escaped_string
-{
- public:
- escaped_string () { m_owned = false; m_str = NULL; };
- ~escaped_string () { if (m_owned) free (m_str); }
- operator const char *() const { return (const char *) m_str; }
- void escape (const char *);
- private:
- char *m_str;
- bool m_owned;
-};
-
/* PR 84195: Replace control characters in "unescaped" with their
escaped equivalents. Allow newlines if -fmessage-length has
been set to a non-zero value. This is done here, rather than
JeanHeyd Meneide July 26, 2019, 1 a.m. UTC | #7
The HTML formatting was off (again), so I used git send-email as someone
recommended to me in the IRC. Patch is here:
https://gcc.gnu.org/ml/gcc-patches/2019-07/msg01670.html

I... think it's good, there? Apologies for all the noise; it's a bit hard
getting used to these tools.

Sincerely,
JeanHeyd

Patch
diff mbox series

diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index e6452542bcc..3db90ec6c66 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,7 @@ 
+2019-07-20  ThePhD  <phdofthehouse@gmail.com>
+
+ * c-family/c-lex.c: increase [[nodiscard]] feature macro value (final
value pending post-Cologne mailing)
+
 2019-07-20  Jakub Jelinek  <jakub@redhat.com>

  * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_LOOP.

diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 851fd704e5d..f2c0b62c95b 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -353,13 +353,14 @@  c_common_has_attribute (cpp_reader *pfile)
       else if (is_attribute_p ("deprecated", attr_name))
  result = 201309;
       else if (is_attribute_p ("maybe_unused", attr_name)
-       || is_attribute_p ("nodiscard", attr_name)
        || is_attribute_p ("fallthrough", attr_name))
  result = 201603;
       else if (is_attribute_p ("no_unique_address", attr_name)
        || is_attribute_p ("likely", attr_name)
        || is_attribute_p ("unlikely", attr_name))
  result = 201803;
+      else if (is_attribute_p ("nodiscard", attr_name))
+ result = 201907; /* placeholder until C++20 Post-Cologne Working Draft. */
       if (result)
  attr_name = NULL_TREE;
     }
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index d645cdef147..9877b1af517 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,17 @@ 
+2019-07-20  ThePhD  <phdofthehouse@gmail.com>
+
+ * escaped_string.h: New. Refactored out of tree.c to make more
+ broadly available (e.g. to parser.c, cvt.c).
+ * tree.c: Implement p1301 - nodiscard("should have a reason"))
+ Moved escaped_string class for working with attributes to
+ dedicated header.
+ Added C++2a nodiscard string message handling.
+ Increase nodiscard argument handling max_length from 0
+ to 1. (error C++2a gated)
+ * parser.c: add requirement that nodiscard only be seen
+ once in attribute-list (C++2a gated)
+ * cvt.c: add nodiscard message to output, if applicable
+
 2019-07-20  Jason Merrill  <jason@redhat.com>

  * cp-tree.h (ovl_iterator::using_p): A USING_DECL by itself was also

diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 23d2aabc483..aa4816f3a4f 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -35,6 +35,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "escaped_string.h"

 static tree convert_to_pointer_force (tree, tree, tsubst_flags_t);
 static tree build_type_conversion (tree, tree);
@@ -1029,19 +1030,29 @@  maybe_warn_nodiscard (tree expr, impl_conv_void
implicit)
   if (implicit != ICV_CAST && fn
       && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
     {
-      auto_diagnostic_group d;
+      tree attr = DECL_ATTRIBUTES (fn);
+      escaped_string msg;
+      if (attr)
+         msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+      bool has_msg = static_cast<bool>(msg);
+    auto_diagnostic_group d;
       if (warning_at (loc, OPT_Wunused_result,
-      "ignoring return value of %qD, "
-      "declared with attribute nodiscard", fn))
- inform (DECL_SOURCE_LOCATION (fn), "declared here");
+                     "ignoring return value of %qD, "
+                     "declared with attribute nodiscard%s%s", fn, (has_msg
? ": " : ""), (has_msg ? (const char*)msg : "")))
+        inform (DECL_SOURCE_LOCATION (fn), "declared here");
     }
   else if (implicit != ICV_CAST
    && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
     {
+      tree attr = TYPE_ATTRIBUTES (rettype);
+      escaped_string msg;
+      if (attr)
+         msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+      bool has_msg = static_cast<bool>(msg);
       auto_diagnostic_group d;
       if (warning_at (loc, OPT_Wunused_result,
-      "ignoring returned value of type %qT, "
-      "declared with attribute nodiscard", rettype))
+                      "ignoring returned value of type %qT, "
+                      "declared with attribute nodiscard%s%s", rettype,
(has_msg ? ": " : ""), (has_msg ? (const char*)msg : "")))
  {
   if (fn)
     inform (DECL_SOURCE_LOCATION (fn),
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 5c379aaf58d..d66d13615fc 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -26399,13 +26399,26 @@  cp_parser_check_std_attribute (tree attributes,
tree attribute)
     {
       tree name = get_attribute_name (attribute);
       if (is_attribute_p ("noreturn", name)
-  && lookup_attribute ("noreturn", attributes))
- error ("attribute %<noreturn%> can appear at most once "
-       "in an attribute-list");
+     && lookup_attribute ("noreturn", attributes))
+        {
+        error ("attribute %<noreturn%> can appear at most once "
+               "in an attribute-list");
+        }
       else if (is_attribute_p ("deprecated", name)
        && lookup_attribute ("deprecated", attributes))
- error ("attribute %<deprecated%> can appear at most once "
-       "in an attribute-list");
+        {
+         error ("attribute %<deprecated%> can appear at most once "
+                "in an attribute-list");
+        }
+      else if (cxx_dialect >= cxx2a)
+        {
+          if (is_attribute_p ("nodiscard", name)
+         && lookup_attribute ("nodiscard", attributes))
+            {
+            error ("attribute %<nodiscard%> can appear at most once "
+                   "in an attribute-list");
+            }
+        }
     }
 }

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 37e24a1669c..8c2d056f3eb 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4356,9 +4356,27 @@  zero_init_p (const_tree t)
    warn_unused_result attribute.  */

 static tree
-handle_nodiscard_attribute (tree *node, tree name, tree /*args*/,
+handle_nodiscard_attribute (tree *node, tree name, tree args,
     int /*flags*/, bool *no_add_attrs)
 {
+  if (!args)
+    *no_add_attrs = true;
+  else if (cxx_dialect >= cxx2a)
+    {
+      if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+        {
+          error ("nodiscard attribute argument must be a string");
+          *no_add_attrs = true;
+        }
+    }
+  else
+    {
+      if (!*no_add_attrs)
+        {
+          error("nodiscard attribute does not take any arguments: use flag
%<-std=c2a%> or better to compile your code");
+        }
+    }
+
   if (TREE_CODE (*node) == FUNCTION_DECL)
     {
       if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
@@ -4447,7 +4465,7 @@  const struct attribute_spec std_attribute_table[] =
        affects_type_identity, handler, exclude } */
   { "maybe_unused", 0, 0, false, false, false, false,
     handle_unused_attribute, NULL },
-  { "nodiscard", 0, 0, false, false, false, false,
+  { "nodiscard", 0, 1, false, false, false, false,
     handle_nodiscard_attribute, NULL },
   { "no_unique_address", 0, 0, true, false, false, false,
     handle_no_unique_addr_attribute, NULL },
diff --git a/gcc/escaped_string.h b/gcc/escaped_string.h
new file mode 100644
index 00000000000..8c9a236da41
--- /dev/null
+++ b/gcc/escaped_string.h
@@ -0,0 +1,41 @@ 
+/* Shared escaped string class.
+   Copyright (C) 1999-2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ESCAPED_STRING_H
+#define GCC_ESCAPED_STRING_H
+
+#include <cstdlib>
+
+/* A class to handle converting a string that might contain
+   control characters, (eg newline, form-feed, etc), into one
+   in which contains escape sequences instead.  */
+
+class escaped_string
+{
+ public:
+  escaped_string () { m_owned = false; m_str = NULL; };
+  ~escaped_string () { if (m_owned) free (m_str); }
+  operator const char *() const { return (const char *) m_str; }
+  void escape (const char *);
+ private:
+  char *m_str;
+  bool  m_owned;
+};
+
+#endif /* ! GCC_ESCAPED_STRING_H */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index adefdb937ab..e521a38eac6 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@ 
+2019-07-20  ThePhD  <phdofthehouse@gmail.com>
+
+ * g++.dg/cpp2a/nodiscard-reason.C: New test.
+ * g++.dg/cpp2a/nodiscard-once.C: New test.
+ * g++.dg/cpp2a/nodiscard-once-clause.C: New test.
+ * g++.dg/cpp2a/nodiscard-bad-clause.C: New test.
+
 2019-07-20  Jakub Jelinek  <jakub@redhat.com>

  * c-c++-common/gomp/cancel-1.c: Adjust expected diagnostic wording.
@@ -16,6 +23,13 @@ 

  * gcc.dg/vect/vect-simd-16.c: New test.

+2019-07-20  ThePhD  <phdofthehouse@gmail.com>
+
+ * g++.dg/cpp2a/nodiscard-reason.C: New test; check nodiscard reason
string is applied.
+ * g++.dg/cpp2a/nodiscard-once.C: New test; check nodiscard can only be
used once.
+ * g++.dg/cpp2a/nodiscard-once-clause.C: New test; check nodiscard
arguments can only have one.
+ * g++.dg/cpp2a/nodiscard-bad-clause.C: New test; check nodiscard does not
accept non-string-literals.
+
 2019-07-19  Jeff Law  <law@redhat.com>

  * gcc.dg/tree-ssa/ssa-dse-37.c: New test.
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
new file mode 100644
index 00000000000..2d242ee7abd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
@@ -0,0 +1,12 @@ 
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard(123)]] int check1 (void); /* { dg-error "nodiscard attribute
argument.*must be a string" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
new file mode 100644
index 00000000000..11b1c6a9128
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
@@ -0,0 +1,12 @@ 
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard("not", "allowed")]] int check1 (void); /* { dg-error "wrong
number of arguments..*nodiscard" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
new file mode 100644
index 00000000000..cf34812ed3f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
@@ -0,0 +1,12 @@ 
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard, nodiscard]] int check1 (void); /* { dg-error
".nodiscard..*can appear at most once" } */
+
+void
+test (void)
+{
+  check1 (); /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
new file mode 100644
index 00000000000..ea29be32ecb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
@@ -0,0 +1,203 @@ 
+/* nodiscard attribute tests, adapted from
gcc.dg/attr-warn-unused-result.c.  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+#define NODIS [[nodiscard("exact_message")]]
+#define NODISAI [[nodiscard("exact_inline_message"), gnu::always_inline]]
inline
+enum [[nodiscard("exact_E_message")]] E { e };
+typedef E (*fnt) (void);
+
+typedef struct { long i; } A;
+typedef struct { long i; long j; } B;
+typedef struct { char big[1024]; fnt fn; } C;
+struct [[nodiscard("exact_D_message")]] D { int i; D(); ~D(); };
+
+NODIS E check1 (void);
+NODIS void check2 (void); /* { dg-warning "10:.nodiscard..*exact_message"
} */
+NODIS int foo; /* { dg-warning "9:.nodiscard..*exact_message" } */
+int bar (void);
+NODISAI E check3 (void) { return (E)bar (); }
+NODIS A check4 (void);
+NODIS B check5 (void);
+NODIS C check6 (void);
+A bar7 (void);
+B bar8 (void);
+C bar9 (void);
+NODISAI A check7 (void) { return bar7 (); }
+NODISAI B check8 (void) { return bar8 (); }
+NODISAI C check9 (void) { return bar9 (); }
+/* This is useful for checking whether return value of statement
+   expressions (returning int in this case) is used.  */
+NODISAI int check_int_result (int res) { return res; }
+#define GU(v) ({ int e = 0; (v) = bar (); if ((v) < 23) e = 14; e; })
+fnt fnptr;
+NODIS E check10 (void);
+int baz (void);
+NODISAI E check11 (void) { return (E)baz (); }
+int k;
+
+D check12();
+
+void
+test (void)
+{
+  int i = 0, j;
+  const fnt pcheck1 = check1;
+  const fnt pcheck3 = check3;
+  A a;
+  B b;
+  C c;
+  D d;
+  if (check1 ())
+    return;
+  i += check1 ();
+  i += ({ check1 (); });
+  check1 (); /* { dg-warning "nodiscard.*exact_message" } */
+  (void) check1 ();
+  check1 (), bar (); /* { dg-warning "nodiscard.*exact_message" } */
+  check2 ();
+  (void) check2 ();
+  check2 (), bar ();
+  if (check3 ())
+    return;
+  i += check3 ();
+  i += ({ check3 (); });
+  check3 (); /* { dg-warning "nodiscard.*exact_inline_message" } */
+  (void) check3 ();
+  check3 (), bar (); /* { dg-warning "nodiscard.*exact_inline_message" } */
+  a = check4 ();
+  if (a.i)
+    return;
+  if (check4 ().i)
+    return;
+  if (({ check4 (); }).i)
+    return;
+  check4 (); /* { dg-warning "nodiscard.*exact_message" } */
+  (void) check4 ();
+  check4 (), bar (); /* { dg-warning "nodiscard.*exact_message" } */
+  b = check5 ();
+  if (b.i + b.j)
+    return;
+  if (check5 ().j)
+    return;
+  if (({ check5 (); }).j)
+    return;
+  check5 (); /* { dg-warning "nodiscard.*exact_message" } */
+  (void) check5 ();
+  check5 (), bar (); /* { dg-warning "nodiscard.*exact_message" } */
+  c = check6 ();
+  if (c.big[12] + c.big[29])
+    return;
+  if (check6 ().big[27])
+    return;
+  if (({ check6 (); }).big[0])
+    return;
+  check6 (); /* { dg-warning "nodiscard.*exact_message" } */
+  (void) check6 ();
+  check6 (), bar (); /* { dg-warning "nodiscard.*exact_message" } */
+  a = check7 ();
+  if (a.i)
+    return;
+  if (check7 ().i)
+    return;
+  if (({ check7 (); }).i)
+    return;
+  check7 (); /* { dg-warning "nodiscard.*exact_inline_message" } */
+  (void) check7 ();
+  check7 (), bar (); /* { dg-warning "nodiscard.*exact_inline_message" } */
+  b = check8 ();
+  if (b.i + b.j)
+    return;
+  if (check8 ().j)
+    return;
+  if (({ check8 (); }).j)
+    return;
+  check8 (); /* { dg-warning "nodiscard.*exact_inline_message" } */
+  (void) check8 ();
+  check8 (), bar (); /* { dg-warning "nodiscard.*exact_inline_message" } */
+  c = check9 ();
+  if (c.big[12] + c.big[29])
+    return;
+  if (check9 ().big[27])
+    return;
+  if (({ check9 (); }).big[0])
+    return;
+  check9 (); /* { dg-warning "nodiscard.*exact_inline_message" } */
+  (void) check9 ();
+  check9 (), bar (); /* { dg-warning "nodiscard.*exact_inline_message" } */
+  if (check_int_result (GU (j)))
+    return;
+  i += check_int_result (GU (j));
+  i += ({ check_int_result (GU (j)); });
+  check_int_result (GU (j)); /* { dg-warning
"nodiscard.*exact_inline_message" } */
+  (void) check_int_result (GU (j));
+  check_int_result (GU (j)), bar (); /* { dg-warning
"nodiscard.*exact_inline_message" } */
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  fnptr = check1;
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  fnptr = check3;
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  if (bar9 ().fn ())
+    return;
+  i += bar9 ().fn ();
+  i += ({ bar9 ().fn (); });
+  bar9 ().fn (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  (void) bar9 ().fn ();
+  bar9 ().fn (), bar (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  if ((k ? check1 : check10) ())
+    return;
+  i += (k ? check1 : check10) ();
+  i += ({ (k ? check1 : check10) (); });
+  (k ? check1 : check10) (); /* { dg-warning "nodiscard.*exact_E_message"
} */
+  (void) (k ? check1 : check10) ();
+  (k ? check1 : check10) (), bar (); /* { dg-warning
"nodiscard.*exact_E_message" } */
+  if ((k ? check3 : check11) ())
+    return;
+  i += (k ? check3 : check11) ();
+  i += ({ (k ? check3 : check11) (); });
+  (k ? check3 : check11) (); /* { dg-warning
"nodiscard.*exact_inline_message" } */
+  (void) (k ? check3 : check11) ();
+  (k ? check3 : check11) (), bar (); /* { dg-warning
"nodiscard.*exact_inline_message" } */
+  if (pcheck1 ())
+    return;
+  i += pcheck1 ();
+  i += ({ pcheck1 (); });
+  pcheck1 (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  (void) pcheck1 ();
+  pcheck1 (), bar (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  if (pcheck3 ())
+    return;
+  i += pcheck3 ();
+  i += ({ pcheck3 (); });
+  pcheck3 (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  (void) pcheck3 ();
+  pcheck3 (), bar (); /* { dg-warning "nodiscard.*exact_E_message" } */
+  d = check12 ();
+  if (d.i)
+    return;
+  if (check12 ().i)
+    return;
+  if (({ check12 (); }).i)
+    return;
+  check12 (); /* { dg-warning "nodiscard.*exact_D_message" } */
+  (void) check12 ();
+  check12 (), bar (); /* { dg-warning "nodiscard.*exact_D_message" } */
+}
diff --git a/gcc/tree.c b/gcc/tree.c
index 8cf75f22220..362e07d1c33 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -33,6 +33,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "backend.h"
 #include "target.h"
 #include "tree.h"
+#include "escaped_string.h"
 #include "gimple.h"
 #include "tree-pass.h"
 #include "ssa.h"
@@ -13132,22 +13133,6 @@  typedef_variant_p (const_tree type)
   return is_typedef_decl (TYPE_NAME (type));
 }

-/* A class to handle converting a string that might contain
-   control characters, (eg newline, form-feed, etc), into one
-   in which contains escape sequences instead.  */
-
-class escaped_string
-{
- public:
-  escaped_string () { m_owned = false; m_str = NULL; };
-  ~escaped_string () { if (m_owned) free (m_str); }
-  operator const char *() const { return (const char *) m_str; }
-  void escape (const char *);
- private:
-  char *m_str;
-  bool  m_owned;
-};
-
 /* PR 84195: Replace control characters in "unescaped" with their
    escaped equivalents.  Allow newlines if -fmessage-length has
    been set to a non-zero value.  This is done here, rather than