The attached patch introduces the get_no_warning(), set_no_warning(),
and copy_no_warning() APIs without making use of them in the rest of
GCC.  They are in three files:

  diagnostic-spec.{h,c}: Location-centric overloads.
  warning-control.cc: Tree- and gimple*-centric overloads.

The location-centric overloads are suitable to use from the diagnostic
subsystem.  The rest can be used from the front ends and the middle end.
Add support for per-location warning groups.

gcc/ChangeLog:

	* Makefile.in (OBJS-libcommon): Add diagnostic-spec.o.
	* gengtype.c (open_base_files): Add diagnostic-spec.h.
	* diagnostic-spec.c: New file.
	* diagnostic-spec.h: New file.
	* warning-control.cc: New file.

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 1164554e6d6..b7bbcb60051 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1696,6 +1696,7 @@ OBJS = \
 	vmsdbgout.o \
 	vr-values.o \
 	vtable-verify.o \
+	warning-control.o \
 	web.o \
 	wide-int.o \
 	wide-int-print.o \
@@ -1707,8 +1708,8 @@ OBJS = \
 
 # Objects in libcommon.a, potentially used by all host binaries and with
 # no target dependencies.
-OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \
-	diagnostic-format-json.o json.o \
+OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \
+	diagnostic-show-locus.o diagnostic-format-json.o json.o \
 	edit-context.o \
 	pretty-print.o intl.o \
 	sbitmap.o \
@@ -2648,6 +2649,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/ipa-modref.h $(srcdir)/ipa-modref.c \
   $(srcdir)/ipa-modref-tree.h \
   $(srcdir)/signop.h \
+  $(srcdir)/diagnostic-spec.h $(srcdir)/diagnostic-spec.c \
   $(srcdir)/dwarf2out.h \
   $(srcdir)/dwarf2asm.c \
   $(srcdir)/dwarf2cfi.c \
diff --git a/gcc/diagnostic-spec.c b/gcc/diagnostic-spec.c
new file mode 100644
index 00000000000..582ae3f3fe2
--- /dev/null
+++ b/gcc/diagnostic-spec.c
@@ -0,0 +1,172 @@
+/* Functions to enable and disable individual warnings on an expression
+   and statement basis.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <mse...@redhat.com>
+
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "bitmap.h"
+#include "tree.h"
+#include "cgraph.h"
+#include "hash-map.h"
+#include "diagnostic-spec.h"
+#include "pretty-print.h"
+
+/* Initialize *THIS from warning option OPT.  */
+
+nowarn_spec_t::nowarn_spec_t (int opt)
+{
+  /* Create a very simple mapping based on testing and experience.
+     It should become more refined with time. */
+  switch (opt)
+    {
+    case 0:
+    case -1:
+      bits = opt;
+      break;
+
+      /* Flow-sensitive warnings about pointer problems issued by both
+	 front ends and the middle end.  */
+    case OPT_Waddress:
+    case OPT_Wnonnull:
+      bits = NW_NONNULL;
+      break;
+
+      /* Flow-sensitive warnings about arithmetic overflow issued by both
+	 front ends and the middle end.  */
+    case OPT_Woverflow:
+    case OPT_Wshift_count_negative:
+    case OPT_Wshift_count_overflow:
+    case OPT_Wstrict_overflow:
+      bits = NW_VFLOW;
+      break;
+
+      /* Lexical warnings issued by front ends.  */
+    case OPT_Wabi:
+    case OPT_Wlogical_op:
+    case OPT_Wparentheses:
+    case OPT_Wreturn_type:
+    case OPT_Wsizeof_array_div:
+    case OPT_Wstrict_aliasing:
+    case OPT_Wunused:
+    case OPT_Wunused_function:
+    case OPT_Wunused_but_set_variable:
+    case OPT_Wunused_variable:
+    case OPT_Wunused_but_set_parameter:
+      bits = NW_LEXICAL;
+      break;
+
+      /* Access warning group.  */
+    case OPT_Warray_bounds:
+    case OPT_Warray_bounds_:
+    case OPT_Wformat_overflow_:
+    case OPT_Wformat_truncation_:
+    case OPT_Wrestrict:
+    case OPT_Wstrict_aliasing_:
+    case OPT_Wstringop_overflow_:
+    case OPT_Wstringop_overread:
+    case OPT_Wstringop_truncation:
+      bits = NW_ACCESS;
+      break;
+
+      /* Initialization warning group.  */
+    case OPT_Winit_self:
+    case OPT_Wuninitialized:
+    case OPT_Wmaybe_uninitialized:
+	bits = NW_UNINIT;
+      break;
+
+    default:
+      /* A catchall group for everything else.  */
+      bits = NW_OTHER;
+    }
+}
+
+/* Map from location to its no-warning disposition.  */
+
+GTY(()) xint_hash_map_t *nowarn_map;
+
+/* Return the no-warning disposition for location LOC and option OPT
+   or for all/any otions by default.  */
+
+bool
+get_no_warning (location_t loc, int opt /* = -1 */)
+{
+  if (!nowarn_map)
+    return false;
+
+  if (const nowarn_spec_t* const pspec = nowarn_map->get (loc))
+    {
+      const nowarn_spec_t optspec (opt);
+      return *pspec & optspec;
+    }
+
+  return false;
+}
+
+/* Set the no-warning disposition for location LOC and option OPT when
+   DIS is true, or clear it when it's false.  Return true if LOC has any
+   warnings disabled at the end of processing.  */
+
+bool
+set_no_warning (location_t loc, int opt /* = -1 */, bool dis /* = true */)
+{
+  const nowarn_spec_t optspec (dis ? opt : 0);
+
+  if (nowarn_spec_t *pspec = nowarn_map ? nowarn_map->get (loc) : NULL)
+    {
+      if (dis)
+	{
+	  *pspec |= optspec;
+	  return true;
+	}
+
+      *pspec &= optspec;
+      if (*pspec)
+	return true;
+
+      nowarn_map->remove (loc);
+      return false;
+    }
+
+  if (!dis)
+    return false;
+
+  if (!nowarn_map)
+    nowarn_map = xint_hash_map_t::create_ggc (32);
+
+  nowarn_map->put (loc, optspec);
+  return true;
+}
+
+/* Copy the no-warning disposition from one location to another.  */
+
+void
+copy_no_warning (location_t to, location_t from)
+{
+  if (!nowarn_map)
+    return;
+
+  if (nowarn_spec_t *pspec = nowarn_map->get (from))
+    nowarn_map->put (to, *pspec);
+  else
+    nowarn_map->remove (to);
+}
diff --git a/gcc/diagnostic-spec.h b/gcc/diagnostic-spec.h
new file mode 100644
index 00000000000..e0c02753923
--- /dev/null
+++ b/gcc/diagnostic-spec.h
@@ -0,0 +1,140 @@
+/* Language-independent APIs to enable/disable per-location warnings.
+
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <mse...@redhat.com>
+
+   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 DIAGNOSTIC_SPEC_H_INCLUDED
+#define DIAGNOSTIC_SPEC_H_INCLUDED
+
+#include "hash-map.h"
+
+/* A "bitset" of warning groups.  */
+
+struct nowarn_spec_t
+{
+  enum
+    {
+     /* Middle end warnings about invalid accesses.  */
+     NW_ACCESS = 1 << 0,
+     /* Front end/lexical warnings.  */
+     NW_LEXICAL = 1 << 1,
+     /* Warnings about null pointers.  */
+     NW_NONNULL = 1 << 2,
+     /* Warnings about uninitialized reads.  */
+     NW_UNINIT = 1 << 3,
+     /* Warnings about arithmetic overflow.  */
+     NW_VFLOW = 1 << 4,
+     /* All other unclassified warnings.  */
+     NW_OTHER = 1 << 5,
+     /* All groups of warnings.  */
+     NW_ALL = (NW_ACCESS | NW_LEXICAL | NW_NONNULL
+	       | NW_UNINIT | NW_VFLOW | NW_OTHER)
+   };
+
+  nowarn_spec_t (): bits () { }
+
+  nowarn_spec_t (int);
+
+  /* Return the raw bitset.  */
+  operator unsigned() const
+  {
+    return bits;
+  }
+
+  /* Return true if the bitset is clear.  */
+  bool operator!() const
+  {
+    return !bits;
+  }
+
+  /* Return the inverse of the bitset.  */
+  nowarn_spec_t operator~() const
+  {
+    nowarn_spec_t res (*this);
+    res.bits &= ~NW_ALL;
+    return res;
+  }
+
+  /* Set *THIS to the bitwise OR of *THIS and RHS.  */
+  nowarn_spec_t& operator|= (const nowarn_spec_t &rhs)
+  {
+    bits |= rhs.bits;
+    return *this;
+  }
+
+  /* Set *THIS to the bitwise AND of *THIS and RHS.  */
+  nowarn_spec_t& operator&= (const nowarn_spec_t &rhs)
+  {
+    bits &= rhs.bits;
+    return *this;
+  }
+
+  /* Set *THIS to the bitwise exclusive OR of *THIS and RHS.  */
+  nowarn_spec_t& operator^= (const nowarn_spec_t &rhs)
+  {
+    bits ^= rhs.bits;
+    return *this;
+  }
+
+private:
+  /* Bitset of warning groups.  */
+  unsigned bits;
+};
+
+/* Return the bitwise OR of LHS and RHS.  */
+
+inline nowarn_spec_t
+operator| (const nowarn_spec_t &lhs, const nowarn_spec_t &rhs)
+{
+  return nowarn_spec_t (lhs) |= rhs;
+}
+
+/* Return the bitwise AND of LHS and RHS.  */
+
+inline nowarn_spec_t
+operator& (const nowarn_spec_t &lhs, const nowarn_spec_t &rhs)
+{
+  return nowarn_spec_t (lhs) &= rhs;
+}
+
+/* Return true if LHS is equal RHS.  */
+
+inline bool
+operator== (const nowarn_spec_t &lhs, const nowarn_spec_t &rhs)
+{
+  return static_cast<unsigned>(lhs) == static_cast<unsigned>(rhs);
+}
+
+/* Return true if LHS is not equal RHS.  */
+
+inline bool
+operator!= (const nowarn_spec_t &lhs, const nowarn_spec_t &rhs)
+{
+  return !(lhs == rhs);
+}
+
+typedef location_t key_type_t;
+typedef int_hash <key_type_t, 0, UINT_MAX> xint_hash_t;
+typedef hash_map<xint_hash_t, nowarn_spec_t> xint_hash_map_t;
+
+/* A mapping from the location of an expression to the warning spec
+   set for it.  */
+extern GTY(()) xint_hash_map_t *nowarn_map;
+
+#endif // DIAGNOSTIC_SPEC_H_INCLUDED
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index b94e2f126ec..c1fa6d35c87 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -1727,7 +1727,7 @@ open_base_files (void)
       "target-globals.h", "ipa-ref.h", "cgraph.h", "symbol-summary.h",
       "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-general.h",
       "omp-offload.h", "ipa-modref-tree.h", "ipa-modref.h", "symtab-thunks.h",
-      "symtab-clones.h",
+      "symtab-clones.h", "diagnostic-spec.h",
       NULL
     };
     const char *const *ifp;
diff --git a/gcc/warning-control.cc b/gcc/warning-control.cc
new file mode 100644
index 00000000000..73147b618ad
--- /dev/null
+++ b/gcc/warning-control.cc
@@ -0,0 +1,230 @@
+/* Functions to enable and disable individual warnings on an expression
+   and statement basis.
+
+   Copyright (C) 2021 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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "bitmap.h"
+#include "tree.h"
+#include "gimple.h"
+#include "cgraph.h"
+#include "hash-map.h"
+#include "diagnostic-spec.h"
+
+/* Return the no-warning bit for EXPR.  */
+
+static inline bool
+get_no_warning_bit (const_tree expr)
+{
+  return expr->base.nowarning_flag;
+}
+
+/* Return the no-warning bit for statement STMT.  */
+
+static inline bool
+get_no_warning_bit (const gimple *stmt)
+{
+  return stmt->no_warning;
+}
+
+/* Set the no-warning bit for EXPR to VALUE.  */
+
+static inline void
+set_no_warning_bit (tree expr, bool value)
+{
+  expr->base.nowarning_flag = value;
+}
+
+/* Set the no-warning bit for statement STMT to VALUE.  */
+
+static inline void
+set_no_warning_bit (gimple *stmt, bool value)
+{
+  stmt->no_warning = value;
+}
+
+/* Return EXPR location of zero.  */
+
+static inline key_type_t
+convert_to_key (const_tree expr)
+{
+  if (DECL_P (expr))
+    return DECL_SOURCE_LOCATION (expr);
+  if (EXPR_P (expr))
+    return EXPR_LOCATION (expr);
+  return 0;
+}
+
+/* Return STMT location (may be zero).  */
+
+static inline key_type_t
+convert_to_key (const gimple *stmt)
+{
+  return gimple_location (stmt);
+}
+
+/* Return the no-warning bitmap for decl/expression EXPR.  */
+
+static nowarn_spec_t*
+get_nowarn_spec (const_tree expr)
+{
+  const key_type_t key = convert_to_key (expr);
+
+  if (!get_no_warning_bit (expr) || !key)
+    return NULL;
+
+  return nowarn_map ? nowarn_map->get (key) : NULL;
+}
+
+/* Return the no-warning bitmap for stateemt STMT.  */
+
+static nowarn_spec_t*
+get_nowarn_spec (const gimple *stmt)
+{
+  const key_type_t key = convert_to_key (stmt);
+
+  if (!get_no_warning_bit (stmt))
+    return NULL;
+
+  return nowarn_map ? nowarn_map->get (key) : NULL;
+}
+
+/* Return true if warning OPT is enabled for decl/expression EXPR.
+   The wildcard OPT of -1 tests for all warnings being disabled.  */
+
+bool
+get_no_warning (const_tree expr, int opt /* = -1 */)
+{
+  const nowarn_spec_t *spec = get_nowarn_spec (expr);
+
+  if (!spec)
+    return get_no_warning_bit (expr);
+
+  nowarn_spec_t optspec (opt);
+  bool dis = *spec & optspec;
+  gcc_assert (get_no_warning_bit (expr) || !dis);
+  return dis;
+}
+
+/* Return true if warning OPT is enabled for statement STMT.
+   The wildcard OPT of -1 tests for all warnings being disabled.  */
+
+bool
+get_no_warning (const gimple *stmt, int opt /* = -1 */)
+{
+  const nowarn_spec_t *spec = get_nowarn_spec (stmt);
+
+  if (!spec)
+    /* Fall back on the single no-warning bit.  */
+    return get_no_warning_bit (stmt);
+
+  nowarn_spec_t optspec (opt);
+  bool dis = *spec & optspec;
+  gcc_assert (get_no_warning_bit (stmt) || !dis);
+  return dis;
+}
+
+/* Enable, or by default disable, a warning for the expression.
+   The wildcard OPT of -1 controls all warnings.  */
+
+void
+set_no_warning (tree expr, int opt /* = -1 */, bool dis /* = true */)
+{
+  const key_type_t key = convert_to_key (expr);
+
+  dis = set_no_warning (key, opt, dis) || dis;
+  set_no_warning_bit (expr, dis);
+}
+
+/* Enable, or by default disable, a warning for the statement STMT.
+   The wildcard OPT of -1 controls all warnings.  */
+
+void
+set_no_warning (gimple *stmt, int opt /* = -1 */, bool dis /* = true */)
+{
+  const key_type_t key = convert_to_key (stmt);
+  const nowarn_spec_t optspec (opt);
+  dis = set_no_warning (key, opt, dis) || dis;
+  set_no_warning_bit (stmt, dis);
+}
+
+/* Copy the warning disposition mapping between an expression and/or
+   a statement.  */
+
+template <class ToType, class FromType>
+void copy_no_warning (ToType to, FromType from)
+{
+  const key_type_t to_key = convert_to_key (to);
+
+  if (nowarn_spec_t *from_map = get_nowarn_spec (from))
+    {
+      /* If there's an entry in the map the no-warning bit must be set.  */
+      gcc_assert (get_no_warning_bit (from));
+
+      if (!nowarn_map)
+	nowarn_map = xint_hash_map_t::create_ggc (32);
+
+      nowarn_map->put (to_key, *from_map);
+      set_no_warning_bit (to, true);
+    }
+  else
+    {
+      if (nowarn_map)
+	nowarn_map->remove (to_key);
+
+      /* The no-warning bit might be set even if there's no entry
+	 in the map.  */
+      set_no_warning_bit (to, get_no_warning_bit (from));
+    }
+}
+
+/* Copy the warning disposition mapping from one expression to another.  */
+
+void
+copy_no_warning (tree to, const_tree from)
+{
+  copy_no_warning<tree, const_tree>(to, from);
+}
+
+/* Copy the warning disposition mapping from a statement to an expression.  */
+
+void
+copy_no_warning (tree to, const gimple *from)
+{
+  copy_no_warning<tree, const gimple *>(to, from);
+}
+
+/* Copy the warning disposition mapping from an expression to a statement.  */
+
+void
+copy_no_warning (gimple *to, const_tree from)
+{
+  copy_no_warning<gimple *, const_tree>(to, from);
+}
+
+/* Copy the warning disposition mapping from one statement to another.  */
+
+void
+copy_no_warning (gimple *to, const gimple *from)
+{
+  copy_no_warning<gimple *, const gimple *>(to, from);
+}

Reply via email to