Skip to content

Commit cc00a9f

Browse files
Merge pull request gcc-mirror#64 from iains/contracts-nonattr-add-contract-scope
Contracts nonattr add contract scope
2 parents 178e18d + 206e0f6 commit cc00a9f

File tree

9 files changed

+166
-121
lines changed

9 files changed

+166
-121
lines changed

gcc/cp/contracts.cc

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -606,14 +606,16 @@ make_postcondition_variable (cp_expr id, tree type)
606606
{
607607
if (id == error_mark_node)
608608
return id;
609+
gcc_checking_assert (scope_chain && scope_chain->bindings
610+
&& scope_chain->bindings->kind == sk_contract);
609611

610612
tree decl = build_lang_decl (PARM_DECL, id, type);
611613
DECL_ARTIFICIAL (decl) = true;
612614
DECL_SOURCE_LOCATION (decl) = id.get_location ();
613615

614-
// constify the postcondition variable
615-
if (flag_contracts_nonattr && !flag_contracts_nonattr_noconst)
616-
TREE_READONLY(decl) = true;
616+
/* Maybe constify the postcondition variable. */
617+
if (flag_contracts_nonattr && should_constify_contract)
618+
TREE_READONLY(decl) = true;
617619

618620
pushdecl (decl);
619621
return decl;
@@ -711,16 +713,24 @@ rebuild_postconditions (tree fndecl)
711713
t = TREE_CHAIN (t))
712714
register_local_identity (t);
713715

716+
begin_scope (sk_contract, fndecl);
717+
bool old_pc = processing_postcondition;
718+
bool old_const = should_constify_contract;
719+
processing_postcondition = true;
720+
should_constify_contract = get_contract_const (contract);
714721
register_local_specialization (newvar, oldvar);
715722

716-
++processing_contract_condition;
717723
condition = tsubst_expr (condition, make_tree_vec (0),
718724
tf_warning_or_error, fndecl);
719-
--processing_contract_condition;
720725

721726
/* Update the contract condition and result. */
722727
POSTCONDITION_IDENTIFIER (contract) = newvar;
723728
CONTRACT_CONDITION (contract) = finish_contract_condition (condition);
729+
processing_postcondition = old_pc;
730+
should_constify_contract = old_const;
731+
gcc_checking_assert (scope_chain && scope_chain->bindings
732+
&& scope_chain->bindings->kind == sk_contract);
733+
pop_bindings_and_leave_scope ();
724734
}
725735
}
726736

@@ -2741,14 +2751,14 @@ constify_contract_access(tree decl)
27412751
/* Do not allow non-const by-value params being used in postconditions. */
27422752

27432753
bool
2744-
maybe_reject_param_in_postcondition(tree decl)
2754+
maybe_reject_param_in_postcondition (tree decl)
27452755
{
27462756
if (flag_contracts_nonattr
2747-
&& processing_contract_postcondition
2748-
&& !dependent_type_p (TREE_TYPE (decl))
27492757
&& !TREE_READONLY (decl)
2750-
&& !CP_TYPE_CONST_P (TREE_TYPE (decl))
27512758
&& TREE_CODE (decl) == PARM_DECL
2759+
&& processing_postcondition
2760+
&& !dependent_type_p (TREE_TYPE (decl))
2761+
&& !CP_TYPE_CONST_P (TREE_TYPE (decl))
27522762
&& !(REFERENCE_REF_P (decl)
27532763
&& TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL))
27542764
{

gcc/cp/cp-tree.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1951,9 +1951,9 @@ struct GTY(()) saved_scope {
19511951
int x_processing_template_decl;
19521952
int x_processing_specialization;
19531953
int x_processing_constraint;
1954-
int x_processing_contract_condition;
1955-
int x_processing_contract_postcondition;
19561954
int suppress_location_wrappers;
1955+
BOOL_BITFIELD x_processing_postcondition : 1;
1956+
BOOL_BITFIELD x_should_constify_contract : 1;
19571957
BOOL_BITFIELD x_processing_explicit_instantiation : 1;
19581958
BOOL_BITFIELD need_pop_function_context : 1;
19591959
BOOL_BITFIELD x_processing_omp_trait_property_expr : 1;
@@ -2031,12 +2031,14 @@ extern GTY(()) struct saved_scope *scope_chain;
20312031
#define processing_omp_trait_property_expr scope_chain->x_processing_omp_trait_property_expr
20322032

20332033
/* Nonzero if we are parsing the conditional expression of a contract
2034-
condition. These expressions appear outside the paramter list (like a
2034+
condition. These expressions appear outside the parameter list (like a
20352035
trailing return type), but are potentially evaluated. */
20362036

2037-
#define processing_contract_condition scope_chain->x_processing_contract_condition
2037+
#define processing_contract_condition \
2038+
(scope_chain->bindings->kind == sk_contract)
20382039

2039-
#define processing_contract_postcondition scope_chain->x_processing_contract_postcondition
2040+
#define processing_postcondition scope_chain->x_processing_postcondition
2041+
#define should_constify_contract scope_chain->x_should_constify_contract
20402042

20412043
#define in_discarded_stmt scope_chain->discarded_stmt
20422044
#define in_consteval_if_p scope_chain->consteval_if_p

gcc/cp/name-lookup.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4630,7 +4630,8 @@ cp_binding_level_descriptor (cp_binding_level *scope)
46304630
"template-parameter-scope",
46314631
"template-explicit-spec-scope",
46324632
"transaction-scope",
4633-
"openmp-scope"
4633+
"openmp-scope",
4634+
"contract-check-scope"
46344635
};
46354636
static_assert (ARRAY_SIZE (scope_kind_names) == sk_count,
46364637
"must keep names aligned with scope_kind enum");
@@ -4720,6 +4721,7 @@ begin_scope (scope_kind kind, tree entity)
47204721
case sk_scoped_enum:
47214722
case sk_transaction:
47224723
case sk_omp:
4724+
case sk_contract:
47234725
case sk_stmt_expr:
47244726
scope->keep = keep_next_level_flag;
47254727
break;

gcc/cp/name-lookup.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ enum scope_kind {
214214
"template <>", this scope is always empty. */
215215
sk_transaction, /* A synchronized or atomic statement. */
216216
sk_omp, /* An OpenMP structured block. */
217+
sk_contract, /* A P2900 contract. */
217218
sk_count /* Number of scope_kind enumerations. */
218219
};
219220

@@ -287,7 +288,7 @@ struct GTY(()) cp_binding_level {
287288
/* The kind of scope that this object represents. However, a
288289
SK_TEMPLATE_SPEC scope is represented with KIND set to
289290
SK_TEMPLATE_PARMS and EXPLICIT_SPEC_P set to true. */
290-
ENUM_BITFIELD (scope_kind) kind : 4;
291+
ENUM_BITFIELD (scope_kind) kind : 5;
291292

292293
/* True if this scope is an SK_TEMPLATE_SPEC scope. This field is
293294
only valid if KIND == SK_TEMPLATE_PARMS. */
@@ -315,7 +316,7 @@ struct GTY(()) cp_binding_level {
315316
parent scope. */
316317
unsigned artificial : 1;
317318

318-
/* 21 bits left to fill a 32-bit word. */
319+
/* 20 bits left to fill a 32-bit word. */
319320
};
320321

321322
/* The binding level currently in effect. */

gcc/cp/parser.cc

Lines changed: 65 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -11815,7 +11815,9 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
1181511815
cp_lexer_consume_token (parser->lexer);
1181611816
first = false;
1181711817

11818-
if (!(at_function_scope_p () || parsing_nsdmi ()))
11818+
if (!(at_function_scope_p ()
11819+
|| parsing_nsdmi ()
11820+
|| current_binding_level->kind == sk_contract))
1181911821
error ("non-local lambda expression cannot have a capture-default");
1182011822
}
1182111823

@@ -13120,16 +13122,12 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
1312013122
0);
1312113123

1312213124
/* Do we have an override for const-ification? */
13123-
bool old_flag_contracts_nonattr_noconst
13124-
= flag_contracts_nonattr_noconst;
13125-
1312613125
bool should_constify = !flag_contracts_nonattr_noconst;
1312713126
if (!modifier.error_p
1312813127
&& (modifier.mutable_p
1312913128
|| (flag_contracts_nonattr_const_keyword
1313013129
&& !modifier.const_p)))
1313113130
should_constify = false;
13132-
flag_contracts_nonattr_noconst = !should_constify;
1313313131

1313413132
/* If we have a current class object, see if we need to consider
1313513133
it const when processing the contract condition. */
@@ -13138,24 +13136,30 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
1313813136
current_class_ref = view_as_const (current_class_ref_copy);
1313913137

1314013138
/* Parse the condition. */
13141-
++processing_contract_condition;
13139+
tree contract = error_mark_node;
13140+
begin_scope (sk_contract, current_function_decl);
13141+
bool old_pc = processing_postcondition;
13142+
bool old_const = should_constify_contract;
13143+
processing_postcondition = false;
13144+
should_constify_contract = should_constify;
1314213145
cp_expr condition = cp_parser_conditional_expression (parser);
13143-
--processing_contract_condition;
13146+
gcc_checking_assert (scope_chain && scope_chain->bindings
13147+
&& scope_chain->bindings->kind == sk_contract);
13148+
/* Build the contract. */
13149+
contract
13150+
= grok_contract (cont_assert, /*mode*/NULL_TREE,
13151+
/*result*/NULL_TREE, condition, loc);
13152+
processing_postcondition = old_pc;
13153+
should_constify_contract = old_const;
13154+
pop_bindings_and_leave_scope ();
1314413155

1314513156
/* Revert (any) constification of the current class object. */
1314613157
current_class_ref = current_class_ref_copy;
1314713158

13148-
flag_contracts_nonattr_noconst
13149-
= old_flag_contracts_nonattr_noconst;
13150-
1315113159
parens.require_close (parser);
13152-
/* Build the contract. */
13153-
tree contract
13154-
= grok_contract (cont_assert, /*mode*/NULL_TREE,
13155-
/*result*/NULL_TREE, condition, loc);
13160+
1315613161
if (contract != error_mark_node)
1315713162
set_contract_const (contract, should_constify);
13158-
1315913163
std_attrs = finish_contract_attribute (cont_assert, contract);
1316013164

1316113165
/* If there are errors in the contract, we do not create the
@@ -31372,19 +31376,6 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute,
3137231376
/* Enable location wrappers when parsing contracts. */
3137331377
auto suppression = make_temp_override (suppress_location_wrappers, 0);
3137431378

31375-
/* Build a fake variable for the result identifier. */
31376-
tree result = NULL_TREE;
31377-
if (identifier)
31378-
{
31379-
begin_scope (sk_block, NULL_TREE);
31380-
result = make_postcondition_variable (identifier);
31381-
++processing_template_decl;
31382-
}
31383-
31384-
bool old_flag_contracts_nonattr_noconst = flag_contracts_nonattr_noconst;
31385-
/* The should_constify value should account for all the mixed flags. */
31386-
flag_contracts_nonattr_noconst = !should_constify;
31387-
3138831379
/* If we have a current class object, see if we need to consider
3138931380
it const when processing the contract condition. */
3139031381
tree current_class_ref_copy = current_class_ref;
@@ -31393,16 +31384,31 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute,
3139331384

3139431385
/* Parse the condition, ensuring that parameters or the return variable
3139531386
aren't flagged for use outside the body of a function. */
31396-
++processing_contract_condition;
31397-
if (postcondition_p)
31398-
++processing_contract_postcondition;
31387+
begin_scope (sk_contract, current_function_decl);
31388+
bool old_pc = processing_postcondition;
31389+
bool old_const = should_constify_contract;
31390+
processing_postcondition = postcondition_p;
31391+
should_constify_contract = should_constify;
31392+
tree result = NULL_TREE;
31393+
if (identifier)
31394+
{
31395+
/* Build a fake variable for the result identifier. */
31396+
result = make_postcondition_variable (identifier);
31397+
++processing_template_decl;
31398+
}
3139931399
cp_expr condition = cp_parser_conditional_expression (parser);
31400-
if (postcondition_p)
31401-
--processing_contract_postcondition;
31400+
/* Build the contract. */
31401+
contract = grok_contract (attribute, mode, result, condition, loc);
31402+
if (identifier)
31403+
--processing_template_decl;
31404+
processing_postcondition = old_pc;
31405+
should_constify_contract = old_const;
31406+
gcc_checking_assert (scope_chain && scope_chain->bindings
31407+
&& scope_chain->bindings->kind == sk_contract);
31408+
pop_bindings_and_leave_scope ();
31409+
3140231410
/* Revert (any) constification of the current class object. */
3140331411
current_class_ref = current_class_ref_copy;
31404-
flag_contracts_nonattr_noconst = old_flag_contracts_nonattr_noconst;
31405-
--processing_contract_condition;
3140631412

3140731413
/* For natural syntax, we eat the parens here. For the attribute
3140831414
syntax, it will be done one level up, we just need to skip to it. */
@@ -31413,15 +31419,6 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute,
3141331419
we need to search the condition for errors. */
3141431420
else if (contains_error_p (condition))
3141531421
cp_parser_skip_up_to_closing_square_bracket (parser);
31416-
31417-
/* Build the contract. */
31418-
contract = grok_contract (attribute, mode, result, condition, loc);
31419-
/* Leave our temporary scope for the postcondition result. */
31420-
if (result)
31421-
{
31422-
--processing_template_decl;
31423-
pop_bindings_and_leave_scope ();
31424-
}
3142531422
}
3142631423

3142731424
if (!flag_contracts)
@@ -31455,22 +31452,16 @@ void cp_parser_late_contract_condition (cp_parser *parser,
3145531452
if (TREE_CODE (contract) == POSTCONDITION_STMT)
3145631453
identifier = POSTCONDITION_IDENTIFIER (contract);
3145731454

31458-
/* Build a fake variable for the result identifier. */
31459-
tree result = NULL_TREE;
31455+
tree type = TREE_TYPE (TREE_TYPE (fn));
3146031456
if (identifier)
3146131457
{
3146231458
/* TODO: Can we guarantee that the identifier has a location? */
3146331459
location_t loc = cp_expr_location (contract);
31464-
tree type = TREE_TYPE (TREE_TYPE (fn));
3146531460
if (!check_postcondition_result (fn, type, loc))
3146631461
{
3146731462
invalidate_contract (contract);
3146831463
return;
3146931464
}
31470-
31471-
begin_scope (sk_block, NULL_TREE);
31472-
result = make_postcondition_variable (identifier, type);
31473-
++processing_template_decl;
3147431465
}
3147531466

3147631467
/* In C++20 contracts, 'this' is not allowed in preconditions of
@@ -31502,7 +31493,6 @@ void cp_parser_late_contract_condition (cp_parser *parser,
3150231493
cp_token_cache *tokens = DEFPARSE_TOKENS (condition);
3150331494
cp_parser_push_lexer_for_tokens (parser, tokens);
3150431495

31505-
bool old_flag_contracts_nonattr_noconst = flag_contracts_nonattr_noconst;
3150631496
bool should_constify = get_contract_const (contract);
3150731497
/* If we have a current class object, see if we need to consider
3150831498
it const when processing the contract condition. */
@@ -31512,17 +31502,32 @@ void cp_parser_late_contract_condition (cp_parser *parser,
3151231502

3151331503
/* Parse the condition, ensuring that parameters or the return variable
3151431504
aren't flagged for use outside the body of a function. */
31515-
++processing_contract_condition;
31516-
if (POSTCONDITION_P (contract))
31517-
++processing_contract_postcondition;
31505+
begin_scope (sk_contract, fn);
31506+
bool old_pc = processing_postcondition;
31507+
bool old_const = should_constify_contract;
31508+
processing_postcondition = POSTCONDITION_P (contract);
31509+
should_constify_contract = should_constify;
31510+
/* Build a fake variable for the result identifier. */
31511+
tree result = NULL_TREE;
31512+
if (identifier)
31513+
{
31514+
result = make_postcondition_variable (identifier, type);
31515+
++processing_template_decl;
31516+
}
3151831517
condition = cp_parser_conditional_expression (parser);
31519-
if (POSTCONDITION_P (contract))
31520-
--processing_contract_postcondition;
31521-
--processing_contract_condition;
31518+
/* Commit to changes. */
31519+
update_late_contract (contract, result, condition);
31520+
/* Leave our temporary scope for the postcondition result. */
31521+
if (identifier)
31522+
--processing_template_decl;
31523+
processing_postcondition = old_pc;
31524+
should_constify_contract = old_const;
31525+
gcc_checking_assert (scope_chain && scope_chain->bindings
31526+
&& scope_chain->bindings->kind == sk_contract);
31527+
pop_bindings_and_leave_scope ();
3152231528

3152331529
if (cp_lexer_next_token_is_not (parser->lexer, CPP_EOF))
31524-
error_at (input_location,
31525-
"expected conditional-expression");
31530+
error_at (input_location, "expected conditional-expression");
3152631531

3152731532
/* Revert to the main lexer. */
3152831533
cp_parser_pop_lexer (parser);
@@ -31533,17 +31538,6 @@ void cp_parser_late_contract_condition (cp_parser *parser,
3153331538
current_class_ref = saved_ccr;
3153431539
current_class_ptr = saved_ccp;
3153531540
contract_class_ptr = saved_contract_ccp;
31536-
31537-
/* Commit to changes. */
31538-
update_late_contract (contract, result, condition);
31539-
flag_contracts_nonattr_noconst = old_flag_contracts_nonattr_noconst;
31540-
31541-
/* Leave our temporary scope for the postcondition result. */
31542-
if (result)
31543-
{
31544-
--processing_template_decl;
31545-
pop_bindings_and_leave_scope ();
31546-
}
3154731541
}
3154831542

3154931543
static contract_modifier

0 commit comments

Comments
 (0)