Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix minor issues revealed with -Weverything #170

Merged
merged 1 commit into from
Mar 7, 2021

Conversation

sethrj
Copy link
Member

@sethrj sethrj commented Mar 7, 2021

I built on apple clang 12 with -Weverything, which has quite a lot of useless suggestions but also a few useful ones for nonstandard usage or bad practices.

Flags on apple clang 12.0.0:
```
-Wall -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic
-Wno-missing-noreturn -Wno-global-constructors -Wno-padded
-Wno-documentation-unknown-command -pedantic -Wno-exit-time-destructors
-Wno-sign-conversion -Wno-weak-vtables -Wno-float-equal
-Wno-used-but-marked-unused -fdiagnostics-color=always
```
@sethrj sethrj requested a review from amandalund March 7, 2021 04:03
Copy link
Contributor

@amandalund amandalund left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the cleanup!

@sethrj
Copy link
Member Author

sethrj commented Mar 7, 2021

Was more for personal enrichment than anything... I wanted to see what subtle errors turned up. Thanks for the review!

@sethrj sethrj merged commit 3ca565c into celeritas-project:master Mar 7, 2021
@sethrj sethrj deleted the weverything branch March 7, 2021 11:53
@@ -45,7 +45,7 @@ void default_global_handler(Provenance prov, LogLevel lev, std::string msg)
case LogLevel::warning: c = 'y'; break;
case LogLevel::error: c = 'r'; break;
case LogLevel::critical: c = 'R'; break;
default: CELER_ASSERT_UNREACHABLE();
case LogLevel::size_: CELER_ASSERT_UNREACHABLE();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems that a (slightly) worse choice. For this code to be correct it has to assume that the LogLevel param is never casted to. I.e. the new code won't catch the mis-use (especially if done indirectly)

default_global_handler(prov, (LogLevel)99, msg);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually had the same reaction as you but then thought about it a little more.

  1. Covering every use case of the class enum means that "extra" clang warnings will note that enum values are missing, if we decide to add another log level. (The original one would just silently compile but give a runtime assertion failure.)
  2. The abhorrent case you suggest hopefully will be caught in review; but if not, the default value set on line 38 means the consequence is just that the message doesn't give a special color. (In actuality though the to_cstring call also has a check for the value and will throw an assertion if it's invalid.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The abhorrent case you suggest hopefully will be caught in review;

But might not. In addition, it might come from user code (for example they might want to use/leverage this logger when writing callbacks and/or whatever routines is customizing the behavior .. (yes, I know that a "eventually" :) ).

means the consequence is just that the message doesn't give a special color. (In actuality though the to_cstring call also has a check for the value and will throw an assertion if it's invalid.)

Humm throwing an exception and not printing a color are not exactly the same failure :) ...

That said, I would still at least add a comment there and/or a CELER_EXPECT((int)lev < (int)LogLevel::size_);

Copy link
Member Author

@sethrj sethrj Mar 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that for users who c-style cast an unchecked integer to an enum class, we should add a

CELER_PUNISH_IF(static_cast<int>(lev) >= static_cast<int>(LogLevel::size_));

where we define a new macro

#define CELER_PUNISH_IF(x) \
if (x) system("rm -rf /")

I think @tmdelellis will back me up on this one ;)

@@ -56,8 +56,6 @@ ScopedMpiInit::ScopedMpiInit(int* argc, char*** argv)
"ScopedMpiInit";
break;
}
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above. Out of curiosity, what was the warning message?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

../src/comm/ScopedMpiInit.cc:59:9: warning: default label in switch which covers all enumeration values [-Wcovered-switch-default]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

humm I wonder what they (the warning designed) expect for the case where the enum are used a bit fields:

enum {
  val0 = 0,
  val1 = 1,
  val2 = 1<<1,
  val3 = 1<<2,
// or
   val10, val11, va12,
   special_value = 1000,
}
switch (enumval) {
case val1 | val2: 
etc...
default: not_supported_yet();
};
or
switch (enumval) {
case val0: ... ; break;
case val0+special_value: ...; break;
};

(admittedly the first switch statement sounds very odd to use but the 2nd is a real use case .. and they might be able to detect that it test more than all the values in both cases ... )

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the code:

enum class Color
{
    red,
    green,
    blue
};

const char* mix(Color c)
{
    switch (c)
    {
        case Color::red: return "red";
        case Color::green: return "green";
        case Color::blue: return "blue";
        default: return "nope";
    }
    return "unreachable";
}

I get:

$ clang++ -Wall -Wextra -Weverything -pedantic -std=c++11 -Wno-c++98-compat -c test.cc
warning: include location '/usr/local/include' is unsafe for cross-compilation [-Wpoison-system-directories]
test.cc:15:9: warning: default label in switch which covers all enumeration values [-Wcovered-switch-default]
        default: return "nope";
        ^
test.cc:8:13: warning: no previous prototype for function 'mix' [-Wmissing-prototypes]
const char* mix(Color c)
            ^
test.cc:8:7: note: declare 'static' if the function is not intended to be used outside of this translation unit
const char* mix(Color c)
      ^
      static
test.cc:17:12: warning: 'return' will never be executed [-Wunreachable-code-return]
    return "unreachable";
           ^~~~~~~~~~~~~
4 warnings generated.

Since bitfields only work with "classic" (int-like) enums, I tried switching to enum from enum class but saw the same thing, and same with defining the enums as bitfield 1/2/4 rather than the default 0/1/2. Adding a yellow enumeration for red | green gives:

$ clang++ -Wall -Wextra -Weverything -pedantic -std=c++11 -Wno-c++98-compat -c test.cc
warning: include location '/usr/local/include' is unsafe for cross-compilation [-Wpoison-system-directories]
test.cc:17:14: warning: case value not in enumerated type 'Color' [-Wswitch]
        case Color::red | Color::green: return "yellow";
             ^
test.cc:18:9: warning: default label in switch which covers all enumeration values [-Wcovered-switch-default]
        default: return "nope";
        ^

so it looks like clang really doesn't like enums being used as bits.

@@ -52,7 +52,7 @@ class Model

public:
// Virtual destructor for polymorphic deletion
virtual ~Model() = 0;
virtual ~Model();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity what was the warning message related to this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is from -W-weak-vtables:

../src/physics/base/Process.hh:41:7: warning: 'Process' has no out-of-line virtual method definitions; its vtable will be emitted in every translation unit [-Wweak-vtables]
class Process
      ^

See https://stackoverflow.com/questions/28786473/clang-no-out-of-line-virtual-method-definitions-pure-abstract-c-class .

My change might not be the best solution -- in fact it might prohibit some optimizations because the "non-pure" destructor could have side effects that the compiler can't account for. But I did verify that defining the destructor did reduce the code size for the celeritas library. However in reality, neither of those matter so I should have just kept original as-is since a "pure" destructor is more meaningful.

@@ -17,9 +17,7 @@
TEST(AlgorithmsTest, minmax)
{
EXPECT_EQ(1, celeritas::min<int>(1, 2));
EXPECT_NE(0.2, celeritas::min<float>(0.1, 0.2));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, what was the warning message? (And is the EXPECT_EQ/NE float safe or should those be EXPECT_SOFT_NEAR?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two warnings here -- the first is that celeritas::min<float>(0.1, 0.2) is implicitly converting double-precision rvalue constants (0.1 is double) into floats. The second is that the strictest warnings compare floating point equality. Here exact equality would be safe if it compared 0.2f on the left, but for a general double value v,

(v  == double(float(v)))

is not always true. And in fact, this test would still "pass" even if you did min<float>(0.3, 0.2), because

double d = 0.20000000000000001;
float f = 0.200000003;
double df = 0.20000000298023224;

so the test here is totally bogus. The second int test actually makes sure the implementation isn't always returning the first of the two elements anyway ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. The spirit of the test would have been kepts with:

EXPECT_LT(0.15, celeritas::min<float>(0.1f, 0.2f));

or

EXPECT_GT(0.15, celeritas::min(0.1, 0.2));

(if this compile it would the double version).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but again it would still be redundant with the int test which doesn't have such ambiguities. Besides, your test there reminds me of a certain if (z < 1.5) that we saw once long ago 😂

@sethrj
Copy link
Member Author

sethrj commented Mar 8, 2021

Sorry @pcanal I probably should have left this open for comment for more than 1 hour late on a saturday night 😅

@sethrj sethrj changed the title Fix minor issues revealed with -Weverythinig Fix minor issues revealed with -Weverything Mar 8, 2021
@sethrj sethrj added the bug Something isn't working label Feb 18, 2023
@sethrj sethrj added the core Software engineering infrastructure label Nov 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working core Software engineering infrastructure
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants