https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127291

            Bug ID: 127291
           Summary: [c++26][contracts] friend declarations do not validate
                    matching contract assertions
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: berne at notadragon dot com
  Target Milestone: ---

Created attachment 65537
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65537&action=edit
Two independent cases -- two friend declarations, and an ordinary declaration
redeclared by a friend -- each accepted with no diagnostic

A friend declaration's contract is never validated against an earlier
declaration of the same function -- although a friend declaration that is
the first declaration will introduce the contract assertions that later
(non-friend) declarations will be compared against.  Two shapes reach
this defect, and neither is diagnosed:

```
// Case 1: two friend declarations of the same function.
struct C {
    friend int f(int x) pre(x > 0);
    friend int f(int x) pre(x < 0);   // accepted; should be a mismatch
};

int f(int x) { return x; }

// Case 2: an ordinary declaration, then a friend redeclaration with a
// different contract -- the friend need not come first, nor does the
// earlier declaration need to be a friend itself.
int h(int x) pre(x > 0);

struct D {
    friend int h(int x) pre(x < 0);   // accepted; should be a mismatch
};

int h(int x) { return x; }
```

```
$ ./gcc-16.2.0/bin/g++ -std=c++26 -c deferred-friend-contract-mismatch.C
$ echo $?
0
```

No diagnostic at all, for either case.  Only the first contract seen for
each function takes effect -- `pre(x > 0)` for both `f` and `h` here.

Non-friend declarations are checked against the first declaration
consistently.

DISCOVERY

Found while implementing redeclaration matching for contracts, checking the
matcher against each path by which one function can be declared twice.  The
friend-inside-a-class path was the one that had no coverage. The ordinary-
declaration-then-friend shape (Case 2 above) was found afterward, while
confirming exactly which redeclaration orderings the defect covers.

ANALYSIS

A friend declaration's contract is still DEFERRED_PARSE at the point
duplicate_decls merges it with the earlier declaration -- a contract on a
member or friend declaration is late-parsed once the class is complete --
and check_redecl_contract skips matching when either side is still
deferred.

Deferring the comparison instead is not the small change it looks like.
duplicate_decls discards whichever declaration is being merged away and
calls remove_decl_with_fn_contracts_specifiers on it, dropping its deferred
contract entirely, before end-of-class late-parsing happens. This is
independent of what the earlier declaration was: an ordinary declaration
discards the friend's contract the same way a first friend declaration
does. By the time the tokens could be parsed there is nothing left to
compare them against -- unless the friend was first, in which case its
own contract has already been late-parsed at end-of-class before anything
later could discard it.

VERSIONS -- all on x86_64-linux-gnu

  source              version                       accepted silently
  compiler-explorer   16.1.0                        yes
  compiler-explorer   16.2.0                        yes
  compiler-explorer   17.0.0 20260909, 919c0d16c91  yes
  local build -g      17.0.0 20260909, 7dab38c9d71  yes

```
$ ./gcc-16.2.0/bin/g++ -v
Using built-in specs.
COLLECT_GCC=./gcc-16.2.0/bin/g++
COLLECT_LTO_WRAPPER=/home/jberne4/repos/compilers/gcc-16.2.0/bin/../libexec/gcc/x86_64-linux-gnu/16.2.0/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../gcc-16.2.0/configure
--prefix=/opt/compiler-explorer/gcc-build/staging --build=x86_64-linux-gnu
--host=x86_64-linux-gnu --target=x86_64-linux-gnu --disable-bootstrap
--enable-multiarch --with-abi=m64 --with-multilib-list=m32,m64,mx32
--enable-multilib --enable-clocale=gnu
--enable-languages=c,c++,fortran,ada,objc,obj-c++,go,d,m2,rust,cobol,algol68
--enable-ld=yes --enable-gold=yes --enable-libstdcxx-time=yes
--enable-linker-build-id --enable-lto --enable-plugins --enable-threads=posix
--with-pkgversion=Compiler-Explorer-Build-gcc--binutils-2.44
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 16.2.0 (Compiler-Explorer-Build-gcc--binutils-2.44)
```

Reply via email to