https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126041
Bug ID: 126041
Summary: [contracts] Source-location line table underflow and
diagnostic metadata corruption in nested generic
lambda contract assertions
Product: gcc
Version: 16.1.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: zuhaitz.zechhub at gmail dot com
Target Milestone: ---
Host: x86_64-redhat-linux
Target: x86_64-redhat-linux
Build: x86_64-redhat-linux
Created attachment 64900
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=64900&action=edit
Preprocessed source.
When a contract_assert evaluates variables captured across multiple nested
generic lambda boundaries (specifically interacting with structured bindings
unpacked from an outer scope), the contract scope-resolution mechanics fail to
resolve implicit reference tracking.
Crucially, the lookup failure completely corrupts the compiler's internal
diagnostic line-number table mapping, forcing the frontend diagnostics to
output garbage negative line numbers (e.g., Location: [filename]:-18826752)
when compiling or handling errors for the assertion block.
EXACT GCC VERSION:
gcc version 16.1.1 20260515 (Red Hat 16.1.1-2) (GCC)
SYSTEM TYPE / TARGET:
Target: x86_64-redhat-linux
COMPLETE COMMAND LINE TRIGGERING THE BUG:
g++ -std=c++26 -fcontracts -save-temps bug_nested_lambda_overflow.cpp
COMPILER OUTPUT:
bug_contract_concepts_sfinae.cpp: In instantiation of ‘void process_data(T)
[with T = EmptyStruct]’:
bug_contract_concepts_sfinae.cpp:12:17: required from here
12 | process_data(e);
| ~~~~~~~~~~~~^~~
bug_contract_concepts_sfinae.cpp:5:46: error: ‘const struct EmptyStruct’ has no
member named ‘nested_value’
5 | pre(requires { obj.nested_value; } ? obj.nested_value > 0 : true)
| ~~~~^~~~~~~~~~~~
zuhaitz@fedora:~/cpp26-contracts/bugs$ g++ -std=c++26 -fcontracts -save-temps
bug_nested_lambda_overflow.cpp
bug_nested_lambda_overflow.cpp: In lambda function:
bug_nested_lambda_overflow.cpp:10:44: error: ‘y’ is not implicitly captured by
a contract assertion [-Wtemplate-body]
10 | contract_assert((x * factor) + y + offset > 0);
| ^
bug_nested_lambda_overflow.cpp:-25147723:35: note: ‘y’ declared here
bug_nested_lambda_overflow.cpp:10:34: error: ‘factor’ is not implicitly
captured by a contract assertion [-Wtemplate-body]
10 | contract_assert((x * factor) + y + offset > 0);
| ^~~~~~
bug_nested_lambda_overflow.cpp:8:34: note: ‘factor’ declared here
8 | auto outer_lambda = [&](auto factor) {
| ~~~~~^~~~~~
bug_nested_lambda_overflow.cpp:10:30: error: ‘x’ is not implicitly captured by
a contract assertion [-Wtemplate-body]
10 | contract_assert((x * factor) + y + offset > 0);
| ^
bug_nested_lambda_overflow.cpp:-25147723:32: note: ‘x’ declared here
MINIMAL STANDALONE TESTCASE (bug_nested_lambda_overflow.cpp):
#include <contracts>
#include <tuple>
int main() {
auto data = std::make_tuple(10, -20);
auto [x, y] = data;
auto outer_lambda = [&](auto factor) {
auto inner_lambda = [&](auto offset) {
contract_assert((x * factor) + y + offset > 0);
};
inner_lambda(5);
};
outer_lambda(2);
}