On 8/17/26 3:39 PM, Jason Merrill wrote:
On 8/14/26 4:40 PM, Wang Jinghao wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu.
-- >8 --
Contract conditions are parsed outside the function body, so
`at_function_scope_p()' is false when their parameter pack expansions
are formed. However, function parameter packs in contract conditions
should use local specializations during substitution, just as they do
within a function body.
Does it work to change at_function_scope_p() to local_bindings_p()? The
latter seems to be the more relevant question.
...it seems not yet, because local_bindings_p wrongly returns false for
contract scope, so we need to move sk_contract before sk_function_parms
in scope_kind, as attached.
gcc/testsuite/ChangeLog:
* g++.dg/contracts/cpp26/pr125645.C: New test.
So perhaps fold1.C.
I pushed the combined patch like so:
From d7a18fca936d64d8c6e7ef7c535e2cfc6665aa8d Mon Sep 17 00:00:00 2001
From: Wang Jinghao <[email protected]>
Date: Fri, 14 Aug 2026 17:27:03 -0400
Subject: [PATCH 02/10] c++/contracts: contract pack expansion referring to
parameter [PR125645]
To: [email protected]
Contract conditions are parsed outside the function body, so
`at_function_scope_p()' is false when their parameter pack expansions
are formed. However, function parameter packs in contract conditions
should use local specializations during substitution, just as they do
within a function body.
local_bindings_p seems like a better test than at_function_scope_p, but we
need to move sk_contract earlier in scope_kind for it to give the right
answer.
PR c++/125645
gcc/cp/ChangeLog:
* cp-tree.h (PACK_INDEX_PARENTHESIZED_P): Fix incorrect
documentation in usage.
* name-lookup.h (enum scope_kind): Move sk_contract before
sk_function_parms.
* pt.cc (make_pack_expansion): Use local_bindings_p.
gcc/testsuite/ChangeLog:
* g++.dg/contracts/cpp26/fold-pr125645.C: New test.
Signed-off-by: Wang Jinghao <[email protected]>
Co-authored-by: Jason Merrill <[email protected]>
---
gcc/cp/cp-tree.h | 2 +-
gcc/cp/name-lookup.h | 6 ++++--
gcc/cp/pt.cc | 7 +++++--
.../g++.dg/contracts/cpp26/fold-pr125645.C | 13 +++++++++++++
4 files changed, 23 insertions(+), 5 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/fold-pr125645.C
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0f989eb1648..75c00f88a08 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -453,7 +453,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
INIT_EXPR_NRV_P (in INIT_EXPR)
ATOMIC_CONSTR_MAP_INSTANTIATED_P (in ATOMIC_CONSTR)
RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR)
- PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*)
MUST_NOT_THROW_NOEXCEPT_P (in MUST_NOT_THROW_EXPR)
CONSTEVAL_BLOCK_P (in STATIC_ASSERT)
LAMBDA_EXPR_CONSTEVAL_BLOCK_P (in LAMBDA_EXPR)
@@ -485,6 +484,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
MUST_NOT_THROW_THROW_P (in MUST_NOT_THROW_EXPR)
LAMBDA_EXPR_CONST_QUAL_P (in LAMBDA_EXPR)
SPLICE_EXPR_MEMBER_ACCESS_P (in SPLICE_EXPR)
+ PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*)
2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE)
ICS_THIS_FLAG (in _CONV)
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index a0e7e10795e..af3fd927f3d 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -239,6 +239,8 @@ enum scope_kind {
sk_cond, /* The scope of the variable declared in the condition
of an if or switch statement. */
sk_stmt_expr, /* GNU statement expression block. */
+ sk_contract, /* A C++26 contract-assertion scope.
+ [basic.scope.contract] */
sk_function_parms, /* The scope containing function parameters. */
sk_class, /* The scope containing the members of a class. */
sk_scoped_enum, /* The scope containing the enumerators of a C++11
@@ -253,8 +255,8 @@ enum scope_kind {
sk_transaction, /* A synchronized or atomic statement. */
sk_omp, /* An OpenMP structured block. */
sk_lambda, /* A lambda scope. */
- sk_contract, /* A C++26 contract-assertion scope.
- [basic.scope.contract] */
+ /* Note that scopes for which local_bindings_p should be true must precede
+ sk_function_parms. */
sk_count /* Number of scope_kind enumerations. */
};
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 81fa2adef55..ef912d1a7a7 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -4402,7 +4402,7 @@ make_pack_expansion (tree arg, tsubst_flags_t complain)
purpose = cxx_make_type (TYPE_PACK_EXPANSION);
PACK_EXPANSION_PATTERN (purpose) = TREE_PURPOSE (arg);
PACK_EXPANSION_PARAMETER_PACKS (purpose) = parameter_packs;
- PACK_EXPANSION_LOCAL_P (purpose) = at_function_scope_p ();
+ PACK_EXPANSION_LOCAL_P (purpose) = local_bindings_p ();
/* Just use structural equality for these TYPE_PACK_EXPANSIONS;
they will rarely be compared to anything. */
@@ -4452,7 +4452,10 @@ make_pack_expansion (tree arg, tsubst_flags_t complain)
}
PACK_EXPANSION_PARAMETER_PACKS (result) = parameter_packs;
- PACK_EXPANSION_LOCAL_P (result) = at_function_scope_p ();
+ /* Contract conditions are parsed outside a function body but function
+ parameter pack expansions in them must use the instantiated parameters
+ rather than dummy declarations. */
+ PACK_EXPANSION_LOCAL_P (result) = local_bindings_p ();
if (ppd.found_extra_args_tree_p)
/* If the pattern of this pack expansion contains a subtree that has
the extra args mechanism for avoiding partial instantiation, then
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/fold-pr125645.C b/gcc/testsuite/g++.dg/contracts/cpp26/fold-pr125645.C
new file mode 100644
index 00000000000..ecc9cf853ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/fold-pr125645.C
@@ -0,0 +1,13 @@
+// PR c++/125645
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=enforce" }
+// { dg-skip-if "requires hosted libstdc++ for stdc++exp" { ! hostedlib } }
+
+template<typename... Args>
+ void f (Args... args)
+ pre (((args) && ...))
+ post (((args) && ...)) {}
+
+int main () {
+ f<const bool> (true);
+}
--
2.55.0