Message ID | 1573867416-55618-30-git-send-email-dmalcolm@redhat.com |
---|---|
State | New |
Headers | show |
Series | RFC: Add a static analysis framework to GCC | expand |
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
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
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 */ >
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 */