[gcc r17-3718] c++/contracts: contract pack expansion referring to parameter [PR125645]

Jason Merrill jason@gcc.gnu.org
Fri Aug 28 00:37:58 GMT 2026


https://gcc.gnu.org/g:d7a18fca936d64d8c6e7ef7c535e2cfc6665aa8d

commit r17-3718-gd7a18fca936d64d8c6e7ef7c535e2cfc6665aa8d
Author: Wang Jinghao <zheng.xianyuwang@gmail.com>
Date:   Fri Aug 14 17:27:03 2026 -0400

    c++/contracts: contract pack expansion referring to parameter [PR125645]
    
    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 <zheng.xianyuwang@gmail.com>
    Co-authored-by: Jason Merrill <jason@redhat.com>

Diff:
---
 gcc/cp/cp-tree.h                                     |  2 +-
 gcc/cp/name-lookup.h                                 |  6 ++++--
 gcc/cp/pt.cc                                         |  7 +++++--
 gcc/testsuite/g++.dg/contracts/cpp26/fold-pr125645.C | 13 +++++++++++++
 4 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0f989eb1648d..75c00f88a083 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 a0e7e10795e7..af3fd927f3d1 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 81fa2adef557..ef912d1a7a7a 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 000000000000..ecc9cf853ac4
--- /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);
+}


More information about the Gcc-cvs mailing list