lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] gcc [[noreturn]] oddities


From: Vadim Zeitlin
Subject: Re: [lmi] gcc [[noreturn]] oddities
Date: Fri, 10 Mar 2017 19:17:46 +0100

On Fri, 10 Mar 2017 13:32:14 +0000 Greg Chicares <address@hidden> wrote:

GC> Looking into this, I observe two oddities, one of which [(1) below]
GC> may be a real concern, while the other [(2) below] seems more comical.
GC> Given that gcc rejects code copied and pasted from the C++11 standard
GC> [N3337], is there a way to use "[[noreturn]]" that is acceptable both
GC> to gcc and also to conforming compilers?
GC> 
GC> (1) Where is "[[noreturn]]" to be written for a function template?
GC> 
GC> C++11 [18.8/1] specifies:
GC> 
GC> [[noreturn]] template <class T> void throw_with_nested(T&& t);

 I think this could have been a mistake as while I do see this in C++11 and
C++14 standards (or, rather, their latest publicly available drafts that I
have), my copy of the latest C++17 draft contains

        template <class T> [[noreturn]] void throw_with_nested(T&& t);

in the same section. But I'm really not sure about what is the Standard
trying to say here. E.g. 7.6.8 [dcl.attr.noreturn] says

        The attribute may be applied to the declarator-id in a function
        declaration.

but it doesn't define what it means to "be applied" and I couldn't find the
formal grammar for the attributes in the function template declarations
neither in 7.6 nor in 14.5.6. So I really don't know whether the synopsis
used in C++1{1,4} is supposed to be correct or not.


 Practically speaking, things are much more clear however:

GC> However, copying and pasting that into an lmi file gives:
GC> 
GC> /opt/lmi/src/lmi/xml_serializable.tpp:189:14: error: expected 
unqualified-id before 'template'
GC>  [[noreturn]] template <class T> void throw_with_nested(T&& t);
GC>               ^

 I can confirm that both gcc (multiple versions from 4.9 to 6.3) and clang
refuse this code, the latter with an even more explicit message:

---------------------------------- >8 --------------------------------------
% cat -n noret.cpp
     1  [[noreturn]]
     2  template <typename T>
     3  void func()
     4  {
     5      throw 17;
     6  }
% clang++-4.0 -std=c++11 -Wall -c noret.cpp
noret.cpp:1:1: error: an attribute list cannot appear here
[[noreturn]]
^~~~~~~~~~~~
1 error generated.
---------------------------------- >8 --------------------------------------

FWIW MSVC does accept this code, but it doesn't really change anything,
especially because the alternative version, with the attribute following
the "template" line and preceding the function declaration, is accepted by
all 3 compilers, so in practice it's clearly the one to use.

GC> And gcc won't accept it at this alternative location:
GC> 
GC> template<typename T>
GC> int xml_serializable<T>::class_version() const
GC> [[noreturn]]
GC> {
GC>     throw "Unreachable--silences a compiler diagnostic.";
GC> }
GC> 
GC> /opt/lmi/src/lmi/xml_serializable.tpp:191:12: error: attribute ignored 
[-Werror=attributes]
GC>  [[noreturn]]
GC>             ^
GC> /opt/lmi/src/lmi/xml_serializable.tpp:191:12: note: an attribute that 
appertains to a type-specifier is ignored

 Again, clang is even more explicit:
---------------------------------- >8 --------------------------------------
% cat -n noret.cpp
     1  template <typename T>
     2  void func()
     3  [[noreturn]]
     4  {
     5      throw 17;
     6  }
% clang++-4.0 -std=c++11 -Wall -c noret.cpp
noret.cpp:3:3: error: 'noreturn' attribute cannot be applied to types
[[noreturn]]
  ^
1 error generated.
---------------------------------- >8 --------------------------------------

I admit it's a mystery to me why does it think this attribute apply to a
type nor even which type does it mean here...

GC> ...so I guess the only options gcc allows are
GC> 
GC> template<typename T>
GC> int xml_serializable<T>::class_version
GC> [[noreturn]]
GC> () const
GC> 
GC> and
GC> 
GC> template<typename T>
GC> [[noreturn]]
GC> int xml_serializable<T>::class_version() const

 Again, this variant is understood by all compilers and seems to be clearly
valid because it is covered by the grammar defining the attributes for
simple (non-template) functions, so from practical point of view, it's the
one to use. I did ask at http://stackoverflow.com/q/42724750/15275 in the
hope that someone knows a more satisfactory answer.


GC> (2) The warning is not idempotent. For example:
GC> 
GC> template<typename T>
GC> [[noreturn]]
GC> int xml_serializable<T>::class_version() const
GC> {
GC>     throw "Unreachable--silences a compiler diagnostic.";
GC> }
GC> 
GC> This is accepted, except with CXXFLAGS='-Wsuggest-attribute=noreturn',
GC> which, oddly enough, repeats the suggestion to use [[noreturn]]:
GC> 
GC> /opt/lmi/src/lmi/xml_serializable.tpp:191:5: error: function might be 
candidate for attribute 'noreturn' [-Werror=suggest-attribute=noreturn]
GC>  int xml_serializable<T>::class_version() const
GC>      ^
GC> 
GC> The outcome is the same if I write "[[noreturn]]" before "()".

 I think this is due to the fact that [[noreturn]] is supposed to be used
on the declaration, not the definition. Again, 7.6.8/1 seems to be not
quite clear to me as it only speaks about what happens if the function is
declared with [[noreturn]] in one place and then declared without it
elsewhere (this makes the program ill-formed), but doesn't mention what
happens if the function is not declared with the attribute but is defined
with it. OTOH maybe definition counts as a declaration too for this
purpose? If so, the program is ill-formed unless [[noreturn]] is used in
both places...

 In any case, if I apply
---------------------------------- >8 --------------------------------------
diff --git a/xml_serializable.hpp b/xml_serializable.hpp
index 88d3505..e5ff68c 100644
--- a/xml_serializable.hpp
+++ b/xml_serializable.hpp
@@ -69,7 +69,9 @@ class LMI_SO xml_serializable
     void immit_members_into(xml::element&) const;

     // Class (T) identification.
+    [[noreturn]]
     virtual int                class_version() const = 0;
+    [[noreturn]]
     virtual std::string const& xml_root_name() const = 0;

     // Reading and writing.
---------------------------------- >8 --------------------------------------

then I don't get the warnings about these functions any more with
-Wsuggest-attribute=noreturn, but I'm not sure if it's the right thing to
do. I think it is because the attributes don't seem to be inherited, so a
derived class should be able to override these functions without using
[[noreturn]] and return values from them without triggering undefined
behaviour, but, again, I can't find any clear proof of this in the
standard and all I can do is that in practice this does work fine with all
3 compilers I tested with.

 Regards,
VZ


reply via email to

[Prev in Thread] Current Thread [Next in Thread]