[29/49] analyzer: new files: tristate.{cc|h}

Message ID 1573867416-55618-30-git-send-email-dmalcolm@redhat.com
State Superseded
Headers show
Series
  • RFC: Add a static analysis framework to GCC
Related show

Commit Message

David Malcolm Nov. 16, 2019, 1:23 a.m.
gcc/ChangeLog:
	* analyzer/tristate.cc: New file.
	* analyzer/tristate.h: New file.
---
 gcc/analyzer/tristate.cc | 222 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/analyzer/tristate.h  |  82 +++++++++++++++++
 2 files changed, 304 insertions(+)
 create mode 100644 gcc/analyzer/tristate.cc
 create mode 100644 gcc/analyzer/tristate.h

-- 
1.8.5.3

Comments

Jeff Law Dec. 7, 2019, 3:03 p.m. | #1
On Fri, 2019-11-15 at 20:23 -0500, David Malcolm wrote:
> gcc/ChangeLog:

> 	* analyzer/tristate.cc: New file.

> 	* analyzer/tristate.h: New file.

Nothing really concerning here.  Seems like a generic facility we'd
like to be able to use elsewhere.   Move outside the analyzer?

jeff
David Malcolm Dec. 9, 2019, 11:16 p.m. | #2
On Sat, 2019-12-07 at 08:03 -0700, Jeff Law wrote:
> On Fri, 2019-11-15 at 20:23 -0500, David Malcolm wrote:

> > gcc/ChangeLog:

> > 	* analyzer/tristate.cc: New file.

> > 	* analyzer/tristate.h: New file.

> Nothing really concerning here.  Seems like a generic facility we'd

> like to be able to use elsewhere.   Move outside the analyzer?


Will do for next iteration.

Dave
Martin Sebor Dec. 10, 2019, 12:59 a.m. | #3
On 11/15/19 6:23 PM, David Malcolm wrote:
> gcc/ChangeLog:

> 	* analyzer/tristate.cc: New file.

> 	* analyzer/tristate.h: New file.

> ---

>   gcc/analyzer/tristate.cc | 222 +++++++++++++++++++++++++++++++++++++++++++++++

>   gcc/analyzer/tristate.h  |  82 +++++++++++++++++

>   2 files changed, 304 insertions(+)

>   create mode 100644 gcc/analyzer/tristate.cc

>   create mode 100644 gcc/analyzer/tristate.h

> 

> diff --git a/gcc/analyzer/tristate.cc b/gcc/analyzer/tristate.cc

> new file mode 100644

> index 0000000..ac16129

> --- /dev/null

> +++ b/gcc/analyzer/tristate.cc

> @@ -0,0 +1,222 @@

> +/* "True" vs "False" vs "Unknown".

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +   Contributed by David Malcolm <dmalcolm@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 "gcc-plugin.h"

> +#include "system.h"

> +#include "coretypes.h"

> +#include "analyzer/tristate.h"

> +#include "selftest.h"

> +

> +const char *

> +tristate::as_string () const

> +{

> +  switch (m_value)

> +    {

> +    default:

> +      gcc_unreachable ();

> +    case TS_UNKNOWN:

> +      return "UNKNOWN";

> +    case TS_TRUE:

> +      return "TRUE";

> +    case TS_FALSE:

> +      return "FALSE";

> +    }

> +}

> +

> +tristate

> +tristate::not_ () const

> +{

> +  switch (m_value)

> +    {

> +    default:

> +      gcc_unreachable ();

> +    case TS_UNKNOWN:

> +      return tristate (TS_UNKNOWN);

> +    case TS_TRUE:

> +      return tristate (TS_FALSE);

> +    case TS_FALSE:

> +      return tristate (TS_TRUE);

> +    }

> +}

> +

> +tristate

> +tristate::or_ (tristate other) const

> +{

> +  switch (m_value)

> +    {

> +    default:

> +      gcc_unreachable ();

> +    case TS_UNKNOWN:

> +      if (other.is_true ())

> +	return tristate (TS_TRUE);

> +      else

> +	return tristate (TS_UNKNOWN);

> +    case TS_FALSE:

> +      return other;

> +    case TS_TRUE:

> +      return tristate (TS_TRUE);

> +    }

> +}

> +

> +tristate

> +tristate::and_ (tristate other) const

> +{

> +  switch (m_value)

> +    {

> +    default:

> +      gcc_unreachable ();

> +    case TS_UNKNOWN:

> +      if (other.is_false ())

> +	return tristate (TS_FALSE);

> +      else

> +	return tristate (TS_UNKNOWN);

> +    case TS_TRUE:

> +      return other;

> +    case TS_FALSE:

> +      return tristate (TS_FALSE);

> +    }

> +}

> +

> +#if CHECKING_P

> +

> +namespace selftest {

> +

> +#define ASSERT_TRISTATE_TRUE(TRISTATE) \

> +  SELFTEST_BEGIN_STMT					\

> +  ASSERT_EQ (TRISTATE, tristate (tristate::TS_TRUE));	\

> +  SELFTEST_END_STMT

> +

> +#define ASSERT_TRISTATE_FALSE(TRISTATE) \

> +  SELFTEST_BEGIN_STMT					\

> +  ASSERT_EQ (TRISTATE, tristate (tristate::TS_FALSE));	\

> +  SELFTEST_END_STMT

> +

> +#define ASSERT_TRISTATE_UNKNOWN(TRISTATE) \

> +  SELFTEST_BEGIN_STMT						\

> +  ASSERT_EQ (TRISTATE, tristate (tristate::TS_UNKNOWN));	\

> +  SELFTEST_END_STMT

> +

> +/* Test tristate's ctors, along with is_*, as_string, operator==, and

> +   operator!=.  */

> +

> +static void

> +test_ctors ()

> +{

> +  tristate u (tristate::TS_UNKNOWN);

> +  ASSERT_FALSE (u.is_known ());

> +  ASSERT_FALSE (u.is_true ());

> +  ASSERT_FALSE (u.is_false ());

> +  ASSERT_STREQ (u.as_string (), "UNKNOWN");

> +

> +  tristate t (tristate::TS_TRUE);

> +  ASSERT_TRUE (t.is_known ());

> +  ASSERT_TRUE (t.is_true ());

> +  ASSERT_FALSE (t.is_false ());

> +  ASSERT_STREQ (t.as_string (), "TRUE");

> +

> +  tristate f (tristate::TS_FALSE);

> +  ASSERT_TRUE (f.is_known ());

> +  ASSERT_FALSE (f.is_true ());

> +  ASSERT_TRUE (f.is_false ());

> +  ASSERT_STREQ (f.as_string (), "FALSE");

> +

> +  ASSERT_EQ (u, u);

> +  ASSERT_EQ (t, t);

> +  ASSERT_EQ (f, f);

> +  ASSERT_NE (u, t);

> +  ASSERT_NE (u, f);

> +  ASSERT_NE (t, f);

> +

> +  tristate t2 (true);

> +  ASSERT_TRUE (t2.is_true ());

> +  ASSERT_EQ (t, t2);

> +

> +  tristate f2 (false);

> +  ASSERT_TRUE (f2.is_false ());

> +  ASSERT_EQ (f, f2);

> +

> +  tristate u2 (tristate::unknown ());

> +  ASSERT_TRUE (!u2.is_known ());

> +  ASSERT_EQ (u, u2);

> +}

> +

> +/* Test && on tristate instances.  */

> +

> +static void

> +test_and ()

> +{

> +  ASSERT_TRISTATE_UNKNOWN (tristate::unknown () && tristate::unknown ());

> +

> +  ASSERT_TRISTATE_FALSE (tristate (false) && tristate (false));

> +  ASSERT_TRISTATE_FALSE (tristate (false) && tristate (true));

> +  ASSERT_TRISTATE_FALSE (tristate (true) && tristate (false));

> +  ASSERT_TRISTATE_TRUE (tristate (true) && tristate (true));

> +

> +  ASSERT_TRISTATE_UNKNOWN (tristate::unknown () && tristate (true));

> +  ASSERT_TRISTATE_UNKNOWN (tristate (true) && tristate::unknown ());

> +

> +  ASSERT_TRISTATE_FALSE (tristate::unknown () && tristate (false));

> +  ASSERT_TRISTATE_FALSE (tristate (false) && tristate::unknown ());

> +}

> +

> +/* Test || on tristate instances.  */

> +

> +static void

> +test_or ()

> +{

> +  ASSERT_TRISTATE_UNKNOWN (tristate::unknown () || tristate::unknown ());

> +

> +  ASSERT_TRISTATE_FALSE (tristate (false) || tristate (false));

> +  ASSERT_TRISTATE_TRUE (tristate (false) || tristate (true));

> +  ASSERT_TRISTATE_TRUE (tristate (true) || tristate (false));

> +  ASSERT_TRISTATE_TRUE (tristate (true) || tristate (true));

> +

> +  ASSERT_TRISTATE_TRUE (tristate::unknown () || tristate (true));

> +  ASSERT_TRISTATE_TRUE (tristate (true) || tristate::unknown ());

> +

> +  ASSERT_TRISTATE_UNKNOWN (tristate::unknown () || tristate (false));

> +  ASSERT_TRISTATE_UNKNOWN (tristate (false) || tristate::unknown ());

> +}

> +

> +/* Test ! on tristate instances.  */

> +

> +static void

> +test_not ()

> +{

> +  ASSERT_TRISTATE_UNKNOWN (!tristate::unknown ());

> +  ASSERT_TRISTATE_FALSE (!tristate (true));

> +  ASSERT_TRISTATE_TRUE (!tristate (false));

> +}

> +

> +/* Run all of the selftests within this file.  */

> +

> +void

> +analyzer_tristate_cc_tests ()

> +{

> +  test_ctors ();

> +  test_and ();

> +  test_or ();

> +  test_not ();

> +}

> +

> +} // namespace selftest

> +

> +#endif /* CHECKING_P */

> diff --git a/gcc/analyzer/tristate.h b/gcc/analyzer/tristate.h

> new file mode 100644

> index 0000000..88b9657

> --- /dev/null

> +++ b/gcc/analyzer/tristate.h

> @@ -0,0 +1,82 @@

> +/* "True" vs "False" vs "Unknown".

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +   Contributed by David Malcolm <dmalcolm@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 GCC_ANALYZER_TRISTATE_H

> +#define GCC_ANALYZER_TRISTATE_H

> +

> +/* "True" vs "False" vs "Unknown".  */


We've all needed or used a type like this a bunch of times, but
not necessarily to denote these three states.  If this becomes
a general-purpose utility I would welcome if it made it possible
to customize the meaning of the states.  If not all of them then
at least the third one.

I don't know if there is a class like this in the C++ Standard
Library but if not, Boost has a tribool class that does let
users redefine the third state.  It might be a model to consider.

Martin

> +

> +class tristate {

> + public:

> +  enum value {

> +    TS_UNKNOWN,

> +    TS_TRUE,

> +    TS_FALSE

> +  };

> +

> +  tristate (enum value val) : m_value (val) {}

> +  tristate (bool val) : m_value (val ? TS_TRUE : TS_FALSE) {}

> +  static tristate unknown () { return tristate (TS_UNKNOWN); }

> +

> +  const char *as_string () const;

> +

> +  bool is_known () const { return m_value != TS_UNKNOWN; }

> +  bool is_true () const { return m_value == TS_TRUE; }

> +  bool is_false () const { return m_value == TS_FALSE; }

> +

> +  tristate not_ () const;

> +  tristate or_ (tristate other) const;

> +  tristate and_ (tristate other) const;

> +

> +  bool operator== (const tristate &other) const


Consider making these operators non-members so that both

   TS_TRUE == state

and

   state == TS_TRUE

are valid.

Martin

> +  {

> +    return m_value == other.m_value;

> +  }

> +

> +  bool operator!= (const tristate &other) const

> +  {

> +    return m_value != other.m_value;

> +  }

> +

> + private:

> +  enum value m_value;

> +};

> +

> +/* Overloaded boolean operators on tristates.  */

> +

> +inline tristate

> +operator ! (tristate t)

> +{

> +  return t.not_ ();

> +}

> +

> +inline tristate

> +operator || (tristate a, tristate b)

> +{

> +  return a.or_ (b);

> +}

> +

> +inline tristate

> +operator && (tristate a, tristate b)

> +{

> +  return a.and_ (b);

> +}

> +

> +#endif /* GCC_ANALYZER_TRISTATE_H */

>

Patch

diff --git a/gcc/analyzer/tristate.cc b/gcc/analyzer/tristate.cc
new file mode 100644
index 0000000..ac16129
--- /dev/null
+++ b/gcc/analyzer/tristate.cc
@@ -0,0 +1,222 @@ 
+/* "True" vs "False" vs "Unknown".
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@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 "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "analyzer/tristate.h"
+#include "selftest.h"
+
+const char *
+tristate::as_string () const
+{
+  switch (m_value)
+    {
+    default:
+      gcc_unreachable ();
+    case TS_UNKNOWN:
+      return "UNKNOWN";
+    case TS_TRUE:
+      return "TRUE";
+    case TS_FALSE:
+      return "FALSE";
+    }
+}
+
+tristate
+tristate::not_ () const
+{
+  switch (m_value)
+    {
+    default:
+      gcc_unreachable ();
+    case TS_UNKNOWN:
+      return tristate (TS_UNKNOWN);
+    case TS_TRUE:
+      return tristate (TS_FALSE);
+    case TS_FALSE:
+      return tristate (TS_TRUE);
+    }
+}
+
+tristate
+tristate::or_ (tristate other) const
+{
+  switch (m_value)
+    {
+    default:
+      gcc_unreachable ();
+    case TS_UNKNOWN:
+      if (other.is_true ())
+	return tristate (TS_TRUE);
+      else
+	return tristate (TS_UNKNOWN);
+    case TS_FALSE:
+      return other;
+    case TS_TRUE:
+      return tristate (TS_TRUE);
+    }
+}
+
+tristate
+tristate::and_ (tristate other) const
+{
+  switch (m_value)
+    {
+    default:
+      gcc_unreachable ();
+    case TS_UNKNOWN:
+      if (other.is_false ())
+	return tristate (TS_FALSE);
+      else
+	return tristate (TS_UNKNOWN);
+    case TS_TRUE:
+      return other;
+    case TS_FALSE:
+      return tristate (TS_FALSE);
+    }
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+#define ASSERT_TRISTATE_TRUE(TRISTATE) \
+  SELFTEST_BEGIN_STMT					\
+  ASSERT_EQ (TRISTATE, tristate (tristate::TS_TRUE));	\
+  SELFTEST_END_STMT
+
+#define ASSERT_TRISTATE_FALSE(TRISTATE) \
+  SELFTEST_BEGIN_STMT					\
+  ASSERT_EQ (TRISTATE, tristate (tristate::TS_FALSE));	\
+  SELFTEST_END_STMT
+
+#define ASSERT_TRISTATE_UNKNOWN(TRISTATE) \
+  SELFTEST_BEGIN_STMT						\
+  ASSERT_EQ (TRISTATE, tristate (tristate::TS_UNKNOWN));	\
+  SELFTEST_END_STMT
+
+/* Test tristate's ctors, along with is_*, as_string, operator==, and
+   operator!=.  */
+
+static void
+test_ctors ()
+{
+  tristate u (tristate::TS_UNKNOWN);
+  ASSERT_FALSE (u.is_known ());
+  ASSERT_FALSE (u.is_true ());
+  ASSERT_FALSE (u.is_false ());
+  ASSERT_STREQ (u.as_string (), "UNKNOWN");
+
+  tristate t (tristate::TS_TRUE);
+  ASSERT_TRUE (t.is_known ());
+  ASSERT_TRUE (t.is_true ());
+  ASSERT_FALSE (t.is_false ());
+  ASSERT_STREQ (t.as_string (), "TRUE");
+
+  tristate f (tristate::TS_FALSE);
+  ASSERT_TRUE (f.is_known ());
+  ASSERT_FALSE (f.is_true ());
+  ASSERT_TRUE (f.is_false ());
+  ASSERT_STREQ (f.as_string (), "FALSE");
+
+  ASSERT_EQ (u, u);
+  ASSERT_EQ (t, t);
+  ASSERT_EQ (f, f);
+  ASSERT_NE (u, t);
+  ASSERT_NE (u, f);
+  ASSERT_NE (t, f);
+
+  tristate t2 (true);
+  ASSERT_TRUE (t2.is_true ());
+  ASSERT_EQ (t, t2);
+
+  tristate f2 (false);
+  ASSERT_TRUE (f2.is_false ());
+  ASSERT_EQ (f, f2);
+
+  tristate u2 (tristate::unknown ());
+  ASSERT_TRUE (!u2.is_known ());
+  ASSERT_EQ (u, u2);
+}
+
+/* Test && on tristate instances.  */
+
+static void
+test_and ()
+{
+  ASSERT_TRISTATE_UNKNOWN (tristate::unknown () && tristate::unknown ());
+
+  ASSERT_TRISTATE_FALSE (tristate (false) && tristate (false));
+  ASSERT_TRISTATE_FALSE (tristate (false) && tristate (true));
+  ASSERT_TRISTATE_FALSE (tristate (true) && tristate (false));
+  ASSERT_TRISTATE_TRUE (tristate (true) && tristate (true));
+
+  ASSERT_TRISTATE_UNKNOWN (tristate::unknown () && tristate (true));
+  ASSERT_TRISTATE_UNKNOWN (tristate (true) && tristate::unknown ());
+
+  ASSERT_TRISTATE_FALSE (tristate::unknown () && tristate (false));
+  ASSERT_TRISTATE_FALSE (tristate (false) && tristate::unknown ());
+}
+
+/* Test || on tristate instances.  */
+
+static void
+test_or ()
+{
+  ASSERT_TRISTATE_UNKNOWN (tristate::unknown () || tristate::unknown ());
+
+  ASSERT_TRISTATE_FALSE (tristate (false) || tristate (false));
+  ASSERT_TRISTATE_TRUE (tristate (false) || tristate (true));
+  ASSERT_TRISTATE_TRUE (tristate (true) || tristate (false));
+  ASSERT_TRISTATE_TRUE (tristate (true) || tristate (true));
+
+  ASSERT_TRISTATE_TRUE (tristate::unknown () || tristate (true));
+  ASSERT_TRISTATE_TRUE (tristate (true) || tristate::unknown ());
+
+  ASSERT_TRISTATE_UNKNOWN (tristate::unknown () || tristate (false));
+  ASSERT_TRISTATE_UNKNOWN (tristate (false) || tristate::unknown ());
+}
+
+/* Test ! on tristate instances.  */
+
+static void
+test_not ()
+{
+  ASSERT_TRISTATE_UNKNOWN (!tristate::unknown ());
+  ASSERT_TRISTATE_FALSE (!tristate (true));
+  ASSERT_TRISTATE_TRUE (!tristate (false));
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+analyzer_tristate_cc_tests ()
+{
+  test_ctors ();
+  test_and ();
+  test_or ();
+  test_not ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/analyzer/tristate.h b/gcc/analyzer/tristate.h
new file mode 100644
index 0000000..88b9657
--- /dev/null
+++ b/gcc/analyzer/tristate.h
@@ -0,0 +1,82 @@ 
+/* "True" vs "False" vs "Unknown".
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@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 GCC_ANALYZER_TRISTATE_H
+#define GCC_ANALYZER_TRISTATE_H
+
+/* "True" vs "False" vs "Unknown".  */
+
+class tristate {
+ public:
+  enum value {
+    TS_UNKNOWN,
+    TS_TRUE,
+    TS_FALSE
+  };
+
+  tristate (enum value val) : m_value (val) {}
+  tristate (bool val) : m_value (val ? TS_TRUE : TS_FALSE) {}
+  static tristate unknown () { return tristate (TS_UNKNOWN); }
+
+  const char *as_string () const;
+
+  bool is_known () const { return m_value != TS_UNKNOWN; }
+  bool is_true () const { return m_value == TS_TRUE; }
+  bool is_false () const { return m_value == TS_FALSE; }
+
+  tristate not_ () const;
+  tristate or_ (tristate other) const;
+  tristate and_ (tristate other) const;
+
+  bool operator== (const tristate &other) const
+  {
+    return m_value == other.m_value;
+  }
+
+  bool operator!= (const tristate &other) const
+  {
+    return m_value != other.m_value;
+  }
+
+ private:
+  enum value m_value;
+};
+
+/* Overloaded boolean operators on tristates.  */
+
+inline tristate
+operator ! (tristate t)
+{
+  return t.not_ ();
+}
+
+inline tristate
+operator || (tristate a, tristate b)
+{
+  return a.or_ (b);
+}
+
+inline tristate
+operator && (tristate a, tristate b)
+{
+  return a.and_ (b);
+}
+
+#endif /* GCC_ANALYZER_TRISTATE_H */