Clang ignores nonnull attributes on pointer-to-member-function declarations

Clang (3.4-trunk) ignores nonnull attributes on pointer-to-member-function declarations.

A test case follows. Recent versions of gcc [1] allow nonnull attributes on pointer-to-member-function declarations. Is there any reason that Clang shouldn't also permit these? If not, I'll open a new PR for this.

Tom.

[1] gcc 5.0, see 60765 – Function attributes ignored for pointer-to-member-function parameters

$ cat t.cpp
struct S;
void (S::*pmf)(int *p1)
     __attribute__((nonnull));
void f(S *s) {
     (s->*pmf)(0);
}

# Clang trunk emits a warning for the presence of the attribute, but not
# for passing a null argument:
$ clang -c t.cpp
t.cpp:3:20: warning: 'nonnull' attribute only applies to functions, methods, and parameters [-Wignored-attributes]
     __attribute__((nonnull));
                    ^
1 warning generated.

# gcc (6.0) accepts the nonnull attribute and emits a warning for
# passing a null argument:
$ gcc -Wall -c t.cpp
t.cpp: In function ‘void f(S*)’:
t.cpp:5:16: warning: null argument where non-null required (argument 2) [-Wnonnull]
      (s->*pmf)(0);
                 ^

You are aware that 3.4-trunk is quite old and we are about to start the
3.7 release cycle?

Joerg

I read that as "from version 3.4 to trunk". And it's true: clang trunk
still behaves as described. But I'm a bit confused, because the referenced
GCC bug report says they made this work:

struct Foo;
void foo (void (Foo::*) () __attribute__ ((nonnull)));

... where the attribute would presumably apply to the pointer-to-member,
and the request is to support this:

void (S::*pmf)(int *p1)
    __attribute__((nonnull));

... where the attribute (again, presumably) jumps over the
pointer-to-member and applies to the pointer parameter? This does not seem
consistent. What is the actual GCC behavior?

    > Clang (3.4-trunk) ignores nonnull attributes on pointer-to-member-function
    > declarations.

    You are aware that 3.4-trunk is quite old and we are about to start the
    3.7 release cycle?

I read that as "from version 3.4 to trunk"

And that is how I intended it.

And it's true: clang trunk
still behaves as described. But I'm a bit confused, because the
referenced GCC bug report says they made this work:

struct Foo;
void foo (void (Foo::*) () __attribute__ ((nonnull)));

... where the attribute would presumably apply to the pointer-to-member,
and the request is to support this:

void (S::*pmf)(int *p1)
     __attribute__((nonnull));

... where the attribute (again, presumably) jumps over the
pointer-to-member and applies to the pointer parameter? This does not
seem consistent. What is the actual GCC behavior?

Interesting. It didn't occur to me that the case in the gcc bug report is ambiguous with respect to Clang's support for infix nonnull attributes.

As specified, the attribute is either an infix attribute specifying that the pointer-to-member-function parameter value must not be null, or it is a non-infix attribute specifying that all of the parameters of the target of the pointer-to-member-function must not be null.

I don't think gcc supports infix nonnull attributes, so I believe the latter behavior is what is intended. gcc compiler warnings appear to bear this out:

$ cat t.cpp
struct S;
void f1(S *s, void (S::*pmf)(void*) __attribute__ ((nonnull))) {
     (s->*pmf)(0);
}
void f2(S *s) {
     f1(s, 0);
}

$ gcc -c -Wall t.cpp
t.cpp: In function ‘void f1(S*, void (S::*)(void*))’:
t.cpp:3:16: warning: null argument where non-null required (argument 2) [-Wnonnull]
      (s->*pmf)(0);
                 ^

Note that there is a warning when passing a null argument with the pointer-to-member-function invocation, but not when passing a null argument as the pointer-to-member-function value.

Tom.

OK, we'll need to figure out how to integrate this into our
__attribute__((nonnull)) semantics. Please do file that PR so we don't
forget!

PR filed:
https://llvm.org/bugs/show_bug.cgi?id=24133

In researching this, I also found that Clang's behavior with regard to nonnull attributes for parameter declarations has changed over time:

$ cat t.cpp
void f1(void (*pf)(void*) __attribute__((nonnull))) {
     pf(0);
}
void f2() {
     f1(0);
}

# Clang 3.4:
clang -c t.cpp
t.cpp:2:9: warning: null passed to a callee which requires a non-null argument [-Wnonnull]
     pf(0);
        ~^
1 warning generated.

# Clang 3.5:
clang -c t.cpp
t.cpp:5:9: warning: null passed to a callee which requires a non-null argument [-Wnonnull]
     f1(0);
        ~^
1 warning generated.

# Clang 3.6:
clang -c t.cpp
t.cpp:2:9: warning: null passed to a callee that requires a non-null argument [-Wnonnull]
     pf(0);
        ~^
t.cpp:5:9: warning: null passed to a callee that requires a non-null argument [-Wnonnull]
     f1(0);
        ~^
2 warnings generated.

Clang 3.4 was consistent with gcc semantics. Clang 3.5 introduced infix nonnull attributes and created an ambiguity that was resolved in favor of applying the attribute to the parameter declaration (changing behavior from Clang 3.4 and breaking consistency with gcc). Clang 3.6 addressed the behavioral deviation by applying the attribute to both the parameter declaration and the function type of the parameter. The Clang 3.6 behavior seems reasonable to me.

Tom.