Skip to content

[BUG] UFCS disable [[nodiscard]] #305

Closed
@filipsajdak

Description

@filipsajdak

In the current implementation of cppfront (aa70d09), the following code:

t2 : type = {
    public x : *int;

    operator=:(out this, p : *int) = {
        x = p;
    }

    ptr1: (inout this, p : *int) -> *int = std::exchange(x, p);
}

ptr_ext: (inout o : t2, p : *int) -> *int = std::exchange(o.x, p);

main :() = {
    n := 42;
    a : t2 = (n&);
    m := 24;
    a.ptr1(m&); // no warning

    ptr_ext(a, m&); // warning: ignoring return value of function declared with 'nodiscard' attribute

    a;
}

Generates (skipping boilerplate):

class t2   {
    public: int* x; 

    public: explicit t2(cpp2::in<int*> p)
        : x{ p }
#line 4 "../tests/bug_ufcs_disable_nodiscard.cpp2"
                                   {

    }
#line 4 "../tests/bug_ufcs_disable_nodiscard.cpp2"
    public: auto operator=(cpp2::in<int*> p) -> t2& {
        x = p;
        return *this;
#line 6 "../tests/bug_ufcs_disable_nodiscard.cpp2"
    }

    public: [[nodiscard]] auto ptr1(cpp2::in<int*> p) -> int* { return std::exchange(x, p); }
};

[[nodiscard]] auto ptr_ext(t2& o, cpp2::in<int*> p) -> int* { return std::exchange(o.x, p); }

auto main() -> int{
    auto n {42}; 
    t2 a {&n}; 
    auto m {24}; 
    CPP2_UFCS(ptr1, a, &m);// no warning

    ptr_ext(a, &m); // warning

    std::move(a);
}

And when compile with cpp1 compiler (clang++-14 in my case):

../tests/bug_ufcs_disable_nodiscard.cpp2:19:5: warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
    ptr_ext(a, &m); // warning
    ^~~~~~~ ~~~~~
1 warning generated.

Only one unused result is identified - CPP2_UFCS(ptr1, a, &m); generates no warning.

The issue is caused by CPP2_UFCS() macros are eventually lambdas called in-place (Immediately Invoked Function Expressions). The lambda has no [[nodiscard]] attribute - this is already proposed: P2173R0. I have checked on my compiler - I modified the macro to (notice that [[nodiscard]] is added after the capture list):

#define CPP2_UFCS(FUNCNAME,PARAM1,...) \
[&][[nodiscard]](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
    if constexpr (requires{ std::forward<decltype(obj)>(obj).FUNCNAME(std::forward<decltype(params)>(params)...); }) { \
        return std::forward<decltype(obj)>(obj).FUNCNAME(std::forward<decltype(params)>(params)...); \
    } else { \
        return FUNCNAME(std::forward<decltype(obj)>(obj), std::forward<decltype(params)>(params)...); \
    } \
}(PARAM1, __VA_ARGS__)

And the result is:

../tests/bug_ufcs_disable_nodiscard.cpp2:17:5: warning: an attribute specifier sequence in this position is a C++2b extension [-Wc++2b-extensions]
    CPP2_UFCS(ptr1, a, &m);// no warning
    ^
include/cpp2util.h:651:4: note: expanded from macro 'CPP2_UFCS'
[&][[nodiscard]](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
   ^
../tests/bug_ufcs_disable_nodiscard.cpp2:17:5: warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
    CPP2_UFCS(ptr1, a, &m);// no warning
    ^~~~~~~~~~~~~~~~~~~~~~
include/cpp2util.h:657:2: note: expanded from macro 'CPP2_UFCS'
}(PARAM1, __VA_ARGS__)
~^~~~~~~~~~~~~~~~~~~~~
../tests/bug_ufcs_disable_nodiscard.cpp2:19:5: warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
    ptr_ext(a, &m); // warning
    ^~~~~~~ ~~~~~
3 warnings generated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions