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)
```