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
