Uploaded image for project: 'Mesos'
  1. Mesos
  2. MESOS-3141

Compiler warning when mocking function type has an enum return type.

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 0.23.0
    • Fix Version/s: 0.24.0
    • Component/s: None
    • Labels:
    • Story Points:
      3

      Description

      The purpose of this ticket is to document a very cryptic error message (actually a warning that gets propagated by -Werror) that gets generated by clang-3.5 from gmock source code when trying to mock a perfectly innocent-looking function.

      Problem

      The following code is attempting to mock a MesosExecutorDriver:

      class MockMesosExecutorDriver : public MesosExecutorDriver {
      public:
        MockMesosExecutorDriver(mesos::Executor* executor)
          : MesosExecutorDriver(executor) {}
      
        MOCK_METHOD1(sendStatusUpdate, Status(const TaskStatus&));
      };
      

      The above code generates the following error message:

      In file included from ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/gmock.h:58:
      In file included from ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/gmock-actions.h:46:
      ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/internal/gmock-internal-utils.h:355:10: error: indirection of non-volatile null pointer will be deleted, not trap [-Werror,-Wnull-dereference]
        return *static_cast<typename remove_reference<T>::type*>(__null);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/gmock-actions.h:78:22: note: in instantiation of function template specialization 'testing::internal::Invalid<mesos::Status>' requested here
          return internal::Invalid<T>();
                           ^
      ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/gmock-actions.h:190:43: note: in instantiation of member function 'testing::internal::BuiltInDefaultValue<mesos::Status>::Get' requested here
              internal::BuiltInDefaultValue<T>::Get() : *value_;
                                                ^
      ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/gmock-spec-builders.h:1435:34: note: in instantiation of member function 'testing::DefaultValue<mesos::Status>::Get' requested here
          return DefaultValue<Result>::Get();
                                       ^
      ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/gmock-spec-builders.h:1334:22: note: in instantiation of member function 'testing::internal::FunctionMockerBase<mesos::Status (const mesos::TaskStatus &)>::PerformDefaultAction' requested here
              func_mocker->PerformDefaultAction(args, call_description));
                           ^
      ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/gmock-spec-builders.h:1448:26: note: in instantiation of function template specialization 'testing::internal::ActionResultHolder<mesos::Status>::PerformDefaultAction<mesos::Status (const mesos::TaskStatus &)>' requested here
          return ResultHolder::PerformDefaultAction(this, args, call_description);
                               ^
      ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/gmock-generated-function-mockers.h:81:7: note: in instantiation of member function 'testing::internal::FunctionMockerBase<mesos::Status (const mesos::TaskStatus &)>::UntypedPerformDefaultAction' requested here
      class FunctionMocker<R(A1)> : public
            ^
      ../3rdparty/libprocess/3rdparty/gmock-1.6.0/include/gmock/internal/gmock-internal-utils.h:355:10: note: consider using __builtin_trap() or qualifying pointer with 'volatile'
        return *static_cast<typename remove_reference<T>::type*>(__null);
               ^
      

      The source of the issue here is that Status is an enum. In gmock-1.6.0/include/gmock/internal/gmock-internal-utils.h you can find the following function:

      template <typename T>
      T Invalid() {
        return *static_cast<typename remove_reference<T>::type*>(NULL);
      }
      

      This function gets called with the return type of a mocked function. In our case, the return type of the mocked function is Status.

      Attempting to compile the following minimal example with clang-3.5 reproduces the error message:

      #include <type_traits>
      
      template <typename T>
      T invalid() {
        return *static_cast<typename std::remove_reference<T>::type *>(nullptr);
      }
      
      enum E { A, B };
      
      int main() {
        invalid<E>();
      }
      

      Note that if the type is not an enum, the warning is not generated. This is why existing mocked functions that return non-enum types such as Future<void> does not encounter this issue.

      Solutions

      The simplest solution is to add -Wno-null-deference to mesos_tests_CPPFLAGS in src/Makefile.am.

      mesos_tests_CPPFLAGS = $(MESOS_CPPFLAGS) -Wno-null-dereference
      

      Another solution is to upgrade gmock from 1.6 to 1.7 because this problem is solved in the newer versions.

      In gmock 1.7

      template <typename T>
      inline T Invalid() {
        return const_cast<typename remove_reference<T>::type&>(
            *static_cast<volatile typename remove_reference<T>::type*>(NULL));
      }
      

      Add volatile could avoid this warning. https://goo.gl/opCiLC

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                haosdent@gmail.com haosdent
                Reporter:
                mcypark Michael Park
              • Votes:
                0 Vote for this issue
                Watchers:
                3 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: