Bug 94554 - spurious -Waddress warning within "if constexpr" function-null compares
Summary: spurious -Waddress warning within "if constexpr" function-null compares
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: 13.0
Assignee: Jason Merrill
URL:
Keywords: diagnostic
Depends on:
Blocks: Waddress
  Show dependency treegraph
 
Reported: 2020-04-10 18:58 UTC by Melissa
Modified: 2022-06-23 15:44 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2022-06-23 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Melissa 2020-04-10 18:58:47 UTC
The following with -std=c++17 -Waddress:

int meow() { return 1; }
void kitty(int);
template <int (*F)()>
void test() {
    if constexpr (F) {
        kitty(F());
    } else {
        kitty(2);
    }
}
template void test<nullptr>();
template void test<meow>();

gives a spurious/pointless warning:

<source>: In instantiation of 'void test() [with int (* F)() = meow]':
<source>:12:26:   required from here
<source>:5:5: warning: the address of 'int meow()' will never be NULL [-Waddress]
    5 |     if constexpr (F) {
      |     ^~

The warning should be suppressed in "if constexpr" contexts, because of course it's going to be always true or always false.

Clang errors on this case, so it's possible that my code is invalid: Is it legal to compare a function pointer against null in a constant-expression?
Comment 1 Marek Polacek 2020-04-10 20:20:39 UTC
I don't think this warning should be suppressed in "if constexpr" contexts.  What's the point of such code?
Comment 2 Melissa 2020-04-10 20:22:27 UTC
Templates that take an optional function pointer as a template parameter.  It lets you have templates that change behavior if a null function pointer is passed.
Comment 3 Daniel Krügler 2020-04-14 21:56:27 UTC
(In reply to Melissa from comment #0)
> Clang errors on this case, so it's possible that my code is invalid: Is it
> legal to compare a function pointer against null in a constant-expression?

The example is ill-formed because the condition of 'if constexpr' is more restricted than that of normal 'if': It expects "a contextually converted
constant expression of type bool" and [expr.const] p10 lists the allowed conversions in this case. This list omits the boolean conversions ([conv.bool]).

But the example would become valid when rewritten as follows:

int meow() { return 1; }
void kitty(int);
template <int (*F)()>
void test() {
    if constexpr (bool(F)) {
        kitty(F());
    } else {
        kitty(2);
    }
}
template void test<nullptr>();
template void test<meow>();
Comment 4 Klaus Rudolph 2020-07-10 15:13:33 UTC
The code can be modified to be well formed if changed to:

if constexpr (F != nullptr )) {

g++ still emits the bogus warning in that case which is a bug I believe!
Comment 5 Marco Trevisan 2020-10-01 17:54:09 UTC
I don't think g++ is totally wrong here,

Actually, if any, it should be stronger in making the error clearer and louder, as per sé it's fine to consider that you're for real not comparing something constant and the function address may not be known at compile time, so not wrong to check.

However, something that will not error but work can be something like:

#include <iostream>
#include <type_traits>

using namespace std;

int meow() { return 1; }
void kitty(int i) { std::cout << "Mowed vlaue " << i << "\n"; }

template <int (*F)()>
void test() {
    using NullType = std::integral_constant<decltype(F), nullptr>;
    using ActualType = std::integral_constant<decltype(F), F>;

    if constexpr (!std::is_same_v<ActualType, NullType>) {
        kitty(F());
    } else {
        kitty(222222);
    }
}

int main()
{
    test<nullptr>();
    test<meow>();

    return 0;
}
Comment 6 Jason Merrill 2022-06-23 04:31:39 UTC
(In reply to Daniel Krügler from comment #3)
> The example is ill-formed because the condition of 'if constexpr' is more
> restricted than that of normal 'if': It expects "a contextually converted
> constant expression of type bool" and [expr.const] p10 lists the allowed
> conversions in this case. This list omits the boolean conversions
> ([conv.bool]).

This was changed by wg21.link/p1401 so the conversion is allowed in this context as well.
Comment 7 GCC Commits 2022-06-23 15:08:03 UTC
The master branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:124a9e08b7a83795bd4d09001955f0eef68ecd00

commit r13-1219-g124a9e08b7a83795bd4d09001955f0eef68ecd00
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Jun 23 00:24:22 2022 -0400

    c++: -Waddress and if constexpr [PR94554]
    
    Like we avoid various warnings for seemingly tautological expressions when
    substituting a template, we should avoid warning for the implicit conversion
    to bool in an if statement.  I considered also doing this for the conditions
    in loop expressions, but that seems unnecessary, as a loop condition is
    unlikely to be a constant.
    
    The change to finish_if_stmt_cond isn't necessary since dependent_operand_p
    looks through IMPLICIT_CONV_EXPR, but makes it more constent with
    e.g. build_x_binary_op that determines the type of an expression and then
    builds it using the original operands.
    
            PR c++/94554
    
    gcc/cp/ChangeLog:
    
            * pt.cc (dependent_operand_p): Split out from...
            (tsubst_copy_and_build): ...here.
            (tsubst_expr) [IF_STMT]: Use it.
            * semantics.cc (finish_if_stmt_cond): Keep the pre-conversion
            condition in the template tree.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp1z/constexpr-if38.C: New test.
Comment 8 Jason Merrill 2022-06-23 15:44:54 UTC
Fixed for GCC 13.