https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127294
Bug ID: 127294
Summary: [c++26][contracts] a member named unqualified in an
explicit-object member function's contract is
diagnosed as a constructor/destructor contract check
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 65539
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65539&action=edit
The four shapes given the wrong message, with four controls including a real
constructor
Naming a non-static data member unqualified in a contract predicate of an
explicit object member function is correctly rejected, but with a message
about a rule that does not apply -- the function is neither a constructor
nor a destructor.
The expected diagnostic is the one the function body already gives for the
same expression: "invalid use of non-static data member 'S::x'". An
unqualified member means (*this).x, and an explicit object member function
has no this ([expr.prim.this]/1).
```
struct S {
int x = 0;
bool ok() const;
};
// (1) Unqualified data member in a precondition.
struct ImplicitThisPre : S {
void f(this ImplicitThisPre& self) pre(x == 0);
};
// (2) The same in a postcondition.
struct ImplicitThisPost : S {
int f(this ImplicitThisPost& self) post(r : x == r);
};
// (3) The same in an assertion-statement in the body.
struct ImplicitThisAssert : S {
void f(this ImplicitThisAssert& self)
{
contract_assert(x == 0);
}
};
// (4) An unqualified member function call in a predicate -- the second of the
// two guards carrying the same defect.
struct ImplicitThisCall : S {
void f(this ImplicitThisCall& self) pre(ok());
};
// CONTROL: the explicit `this' spelling was always right --
// "'this' is unavailable for explicit object member functions".
struct ExplicitThis : S {
void f(this ExplicitThis& self) pre(this->x == 0);
};
// CONTROL: naming the member through the explicit object parameter is of
// course fine, and must keep compiling.
struct ViaSelf : S {
void f(this ViaSelf& self) pre(self.x == 0) pre(self.ok());
};
// CONTROL: an ordinary implicit-object member function may name `this' and
// its members unqualified.
struct ImplicitObject : S {
void g() const pre(this->x == 0) pre(x == 0);
};
// CONTROL: a genuine constructor precondition is where that message belongs,
// and it must keep being produced there.
struct RealCtor : S {
RealCtor() pre(x == 0);
};
```
```
$ ./gcc-16.2.0/bin/g++ -std=c++26 -fsyntax-only \
xobj-member-in-predicate-ctor-message.cpp
xobj-member-in-predicate-ctor-message.cpp: In explicit object member function
'void ImplicitThisPre::f(this ImplicitThisPre&)':
xobj-member-in-predicate-ctor-message.cpp:22:44: error: 'S::x' 'this' required
when accessing a member within a constructor precondition or destructor
postcondition contract check
22 | void f(this ImplicitThisPre& self) pre(x == 0);
| ^
xobj-member-in-predicate-ctor-message.cpp: In explicit object member function
'int ImplicitThisPost::f(this ImplicitThisPost&)':
xobj-member-in-predicate-ctor-message.cpp:27:49: error: 'S::x' 'this' required
when accessing a member within a constructor precondition or destructor
postcondition contract check
27 | int f(this ImplicitThisPost& self) post(r : x == r);
| ^
xobj-member-in-predicate-ctor-message.cpp: In explicit object member function
'void ImplicitThisAssert::f(this ImplicitThisAssert&)':
xobj-member-in-predicate-ctor-message.cpp:34:25: error: 'S::x' 'this' required
when accessing a member within a constructor precondition or destructor
postcondition contract check
34 | contract_assert(x == 0);
| ^
xobj-member-in-predicate-ctor-message.cpp: In explicit object member function
'void ImplicitThisCall::f(this ImplicitThisCall&)':
xobj-member-in-predicate-ctor-message.cpp:41:45: error: 'bool S::ok() const'
'this' required when accessing a member within a constructor precondition or
destructor postcondition contract check
41 | void f(this ImplicitThisCall& self) pre(ok());
| ^~
xobj-member-in-predicate-ctor-message.cpp: In explicit object member function
'void ExplicitThis::f(this ExplicitThis&)':
xobj-member-in-predicate-ctor-message.cpp:47:41: error: 'this' is unavailable
for explicit object member functions
47 | void f(this ExplicitThis& self) pre(this->x == 0);
| ^~~~
xobj-member-in-predicate-ctor-message.cpp:47:31: note: use explicit object
parameter 'self' instead
47 | void f(this ExplicitThis& self) pre(this->x == 0);
| ~~~~~~~~~~~~~~~~~~~^~~~
xobj-member-in-predicate-ctor-message.cpp: In constructor
'RealCtor::RealCtor()':
xobj-member-in-predicate-ctor-message.cpp:65:20: error: 'S::x' 'this' required
when accessing a member within a constructor precondition or destructor
postcondition contract check
65 | RealCtor() pre(x == 0);
| ^
```
Four shapes are affected -- a precondition, a postcondition, an
assertion-statement, and an unqualified member function call -- and the
message is the same wrong one in each.
ExplicitThis, spelling `this->x` outright, gets the right words in the
diagnostic: "'this' is unavailable for explicit object member functions",
with a note suggesting the explicit object parameter. RealCtor, a
genuine constructor precondition, is where that constructor/destructor
message belongs and must keep producing it.
DISCOVERY
Found by an audit comparing every test in a C++26 contracts implementation's
own suite against stock trunk, looking for cases that pass locally only
because they were fixed locally. The member-function-call shape (4) had no
test coverage at all on either side.
ANALYSIS
Two guards in the contracts code reach for the constructor/destructor
message when an unqualified member cannot be resolved in a predicate -- one
for a data member, one for a member function call. Both assume the only way
to arrive there is from a constructor's precondition or a destructor's
postcondition. An explicit object member function is a third way in, and
neither guard checks.
VERSIONS -- all on x86_64-linux-gnu
source version wrong message
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)
```