Bug 1562745

Summary: GCC 7.2 compilation fails over noexcept
Product: Red Hat Developer Toolset Reporter: Piyush Bhoot <pbhoot>
Component: gccAssignee: Marek Polacek <mpolacek>
Status: CLOSED ERRATA QA Contact: Michael Petlan <mpetlan>
Severity: medium Docs Contact:
Priority: medium    
Version: DTS 7.1 RHEL 7CC: jakub, jason, jwakely, kanderso, law, mcermak, mnewsome, mpetlan, ohudlick
Target Milestone: alpha   
Target Release: 7.1   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: devtoolset-8-gcc-8.1.1-5.el7 Doc Type: Bug Fix
Doc Text:
Previously, the compiler rejected code that contained multiple declarations of malloc, with different non-throwing exception specification, that had been accepted. The problem occurred when merging the two declarations. The code has been adjusted in such a way that the two declarations are merged properly and the code compiles.
Story Points: ---
Clone Of: Environment:
Last Closed: 2018-11-13 08:38:30 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Piyush Bhoot 2018-04-02 10:49:59 UTC
Description of problem:
GCC 7.2 compilation fails over noexcept

Version-Release number of selected component (if applicable):
devtoolset-7-gcc-c++-7.2.1-1

How reproducible:
Always

Steps to Reproduce:
1. 
// no.C

#include <type_traits>
#include <iostream>
#include <atomic>
#include <stdlib.h>
#include <dlfcn.h>

  template <typename Return, typename... Args>
  static Return (*rtld_next(Return (*f)(Args...), const char* symbolVersion = nullptr))(Args...)
  {
    Dl_info info;
    dladdr(reinterpret_cast<void*>(f), &info);
    auto symbolName = info.dli_sname;

    // Clear any existing error
    dlerror();

    auto realFunction = reinterpret_cast<decltype(f)>(
      symbolVersion ? dlvsym(RTLD_NEXT, symbolName, symbolVersion)
                    : dlsym(RTLD_NEXT, symbolName));

    return realFunction;
  }

  template <typename F, F* Func, typename... Args>
  static typename std::result_of<F*(Args...)>::type call_next(Args&&... args)
  {
    static std::atomic<F*> real{};

    if (!real.load(std::memory_order_relaxed))
      real.store(rtld_next(Func));

    return real.load(std::memory_order_relaxed)(std::forward<Args>(args)...);
  }


void* malloc(size_t size)
{
  return call_next<decltype(malloc), malloc>(size);
}
void* operator new(std::size_t size)
{
  return call_next < decltype(malloc), ::operator new>(size);
}
int main()
{
}

Compile with :   g++ -std=c++17 -Werror -Wextra no.C 


Actual results:

no2.C: In function 'void* operator new(std::size_t)':
no2.C:43:60: error: no matching function for call to 'call_next<void*(size_t) noexcept, operator new>(std::size_t&)'
   return call_next < decltype(malloc), ::operator new>(size);
                                                            ^
no2.C:25:53: note: candidate: template<class F, F* Func, class ... Args> typename std::result_of<F*(Args ...)>::type call_next(Args&& ...)
   static typename std::result_of<F*(Args...)>::type call_next(Args&&... args)
                                                     ^~~~~~~~~
no2.C:25:53: note:   template argument deduction/substitution failed:
no2.C:43:60: error: ignoring attributes on template argument 'void*(size_t) noexcept {aka void*(long unsigned int) noexcept}' [-Werror=ignored-attributes]
   return call_next < decltype(malloc), ::operator new>(size);
                                                            ^
no2.C:43:60: error: could not convert template argument 'operator new' from '<unresolved overloaded function type>' to 'void* (*)(long unsigned int) noexcep '
no2.C: In instantiation of 'typename std::result_of<F*(Args ...)>::type call_next(Args&& ...) [with F = void*(long unsigned int) noexcept; F* Func = malloc; Args = {long unsigned int&}; typename std::result_of<F*(Args ...)>::type = void*]':
no2.C:38:50:   required from here
no2.C:30:27: error: invalid conversion from 'void* (*)(long unsigned int)' to 'std::atomic<void* (*)(long unsigned int) noexcept>::__pointer_type {aka void* (*)(long unsigned int) noexcept}' [-fpermissive]
       real.store(rtld_next(Func));
                  ~~~~~~~~~^~~~~~
In file included from no2.C:3:0:
/ms/dist/mstk/PROJ/rhdevtoolset/7.0-rhel6-0/.exec/@sys/include/c++/7/atomic:441:7: note:   initializing argument 1 of 'void std::atomic<_Tp*>::store(std::atomic<_Tp*>::__pointer_type, std::memory_order) [with _Tp = void*(long unsigned int) noexcept; std::atomic<_Tp*>::__pointer_type = void* (*)(long unsigned int) noexcept; std::memory_order = std::memory_order]'
       store(__pointer_type __p,
       ^~~~~
cc1plus: all warnings being treated as errors

Expected results:

No warnings

Additional info:

- Same code compilation goes fine on devtoolset-6-gcc-c++-6.3.1-3.1 with -std=c++17 

- No issue with -std=c++14

Comment 2 Marek Polacek 2018-04-02 20:22:24 UTC
The reason we're seeing an error here is that since C++17 'noexcept' is now part of the type system, which means that e.g.

void call (void (*)() noexcept);
void f ();

void
bar ()
{
  call (f);
}

doesn't compile in C++17, but this is only enforced in G++ 7 and newer (starting with https://gcc.gnu.org/r241944).  A noexcept function can be converted to non-noexcept, but not the other way around--hence the invalid conversion from 'void* (*)(long unsigned int)' to 'void* (*)(long unsigned int) noexcept'.  I think the solution is to add a new noexcept overload, or add the noexcept specifier.

Your testcase will compile with G++ 7 with -fno-builtin-malloc, because then the non-noexcept version of malloc defined in the program will be used.

Comment 4 Jason Merrill 2018-04-03 17:42:11 UTC
Fixed upstream in gcc.gnu.org/r259042

Comment 8 Marek Polacek 2018-07-13 17:50:15 UTC
Fixed.

Comment 10 Michael Petlan 2018-11-06 17:37:26 UTC
VERIFIED for devtoolset-8-gcc-8.2.1-3.

Comment 12 errata-xmlrpc 2018-11-13 08:38:30 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2018:3562