Skip to content

Commit

Permalink
c++, contracts: Revise handling of const-ification.
Browse files Browse the repository at this point in the history
This:
1. Revises the handling so that we only need to preserve a single
   tree flag which denotes the requirement to const-ify the
   contract content.
2. Uses the saved flag instead of recomputing the condition in
   delayed parses and template substitution.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
  • Loading branch information
iains committed Jan 24, 2025
1 parent b913973 commit 996b8d8
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 82 deletions.
28 changes: 6 additions & 22 deletions gcc/cp/cp-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -8942,36 +8942,20 @@ set_contract_semantic (tree t, contract_semantic semantic)
TREE_LANG_FLAG_0 (t) = (semantic & 0x04) >> 2;
}

/* Returns the mutable flag of the node. */
/* Returns the const-ify flag of the node. */

inline int
get_contract_mutable (const_tree t)
{
return TREE_LANG_FLAG_4 (CONTRACT_CHECK (t));
}

/* Sets the mutable flag of the node. */

inline void
set_contract_mutable (tree t, int mut)
{
TREE_LANG_FLAG_4 (CONTRACT_CHECK (t)) = mut;
}

/* Returns the mutable flag of the node. */

inline int
inline bool
get_contract_const (const_tree t)
{
return TREE_LANG_FLAG_5 (CONTRACT_CHECK (t));
return TREE_LANG_FLAG_4 (CONTRACT_CHECK (t));
}

/* Sets the mutable flag of the node. */
/* Sets the const-ify flag of the node. */

inline void
set_contract_const (tree t, int mut)
set_contract_const (tree t, bool constify)
{
TREE_LANG_FLAG_5 (CONTRACT_CHECK (t)) = mut;
TREE_LANG_FLAG_4 (CONTRACT_CHECK (t)) = constify;
}

/* Inline bodies. */
Expand Down
90 changes: 37 additions & 53 deletions gcc/cp/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13150,19 +13150,17 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,

parens.require_close (parser);
/* Build the contract. */
tree contract = grok_contract (cont_assert, NULL_TREE /*mode*/,
NULL_TREE /*result*/, condition, loc);
if (contract != error_mark_node && !modifier.error_p)
{
set_contract_mutable (contract, modifier.mutable_p);
set_contract_const (contract, modifier.const_p);
}
tree contract
= grok_contract (cont_assert, /*mode*/NULL_TREE,
/*result*/NULL_TREE, condition, loc);
if (contract != error_mark_node)
set_contract_const (contract, should_constify);

std_attrs = finish_contract_attribute (cont_assert, contract);

/* If there are errors in the contract, we do not create the
attribute tree. This assumes no attributes on
'contract_assert' */
attribute tree. This assumes no attributes on
'contract_assert'. */
if (std_attrs == error_mark_node)
std_attrs = NULL_TREE;
else if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
Expand Down Expand Up @@ -31317,27 +31315,40 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute,
if (attr_mode || identifier)
cp_parser_require (parser, CPP_COLON, RT_COLON);

/* Defer the parsing of pre/post contracts inside class definitions. */
bool should_constify = false;
/* When we have P2900 semantics in force.
Do we have an override for const-ification? This applies equally to
deferred or immediate parses. */
if (flag_contracts_nonattr)
{
should_constify = !flag_contracts_nonattr_noconst;
if (!modifier.error_p
&& (modifier.mutable_p
|| (flag_contracts_nonattr_const_keyword && !modifier.const_p)))
should_constify = false;
}

tree contract;
if (!assertion_p &&
current_class_type &&
TYPE_BEING_DEFINED (current_class_type))
{
/* Skip until we reach an unenclose ']'. If we ran into an unnested ']'
that doesn't close the attribute, return an error and let the attribute
handling code emit an error for missing ']]'. */
/* Defer the parsing of pre/post contracts inside class definitions. */
cp_token *first = cp_lexer_peek_token (parser->lexer);
if (attr_mode)
{
/* Skip until we reach a closing token ]. */
cp_parser_skip_to_closing_parenthesis_1 (parser,
/*recovering=*/false,
CPP_CLOSE_SQUARE,
/*consume_paren=*/false);
if (cp_lexer_peek_token (parser->lexer)->type != CPP_CLOSE_SQUARE
|| cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_CLOSE_SQUARE)
return error_mark_node;
/* Otherwise the closing ]] will be consumed by the caller. */
}
else
/* Skip until we reach a closing token ). */
cp_parser_skip_to_closing_parenthesis_1 (parser,
/*recovering=*/false,
CPP_CLOSE_PAREN,
Expand Down Expand Up @@ -31371,19 +31382,13 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute,
}

bool old_flag_contracts_nonattr_noconst = flag_contracts_nonattr_noconst;

/* Do we have an override for const-ification? */
bool should_constify = !flag_contracts_nonattr_noconst;
if (!modifier.error_p
&& (modifier.mutable_p
|| (flag_contracts_nonattr_const_keyword && !modifier.const_p)))
should_constify = false;
/* The should_constify value should account for all the mixed flags. */
flag_contracts_nonattr_noconst = !should_constify;

/* If we have a current class object, see if we need to consider
it const when processing the contract condition. */
tree current_class_ref_copy = current_class_ref;
if (flag_contracts_nonattr && should_constify && current_class_ref_copy)
if (should_constify && current_class_ref_copy)
current_class_ref = view_as_const (current_class_ref_copy);

/* Parse the condition, ensuring that parameters or the return variable
Expand Down Expand Up @@ -31425,21 +31430,9 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute,
return error_mark_node;
}

/* Save the decision about const-ification. */
if (contract != error_mark_node)
{
set_contract_mutable (contract, false);
set_contract_const (contract, false);
if (flag_contracts_nonattr && !attr_mode)
{
if (!modifier.error_p)
{
set_contract_mutable (contract, modifier.mutable_p);
set_contract_const (contract, modifier.const_p);
}
else
set_contract_const (contract, !flag_contracts_nonattr_noconst);
}
}
set_contract_const (contract, should_constify);

return finish_contract_attribute (attribute, contract);
}
Expand Down Expand Up @@ -31480,15 +31473,7 @@ void cp_parser_late_contract_condition (cp_parser *parser,
++processing_template_decl;
}

bool mutable_p = get_contract_mutable (contract);
bool const_p = get_contract_const (contract);
bool old_flag_contracts_nonattr_noconst = flag_contracts_nonattr_noconst;
if (flag_contracts_nonattr
&& (mutable_p
|| (flag_contracts_nonattr_const_keyword && !const_p)))
flag_contracts_nonattr_noconst = true;

/* In C++"0 contracts, 'this' is not allowed in preconditions of
/* In C++20 contracts, 'this' is not allowed in preconditions of
constructors or in postconditions of destructors. Note that the
previous value of this variable is established by the calling function,
so we need to save it here. P2900 contracts allow access to members
Expand All @@ -31509,22 +31494,21 @@ void cp_parser_late_contract_condition (cp_parser *parser,
}
}
else
{
contract_class_ptr = NULL_TREE;
}
contract_class_ptr = NULL_TREE;

push_unparsed_function_queues (parser);

/* Push the saved tokens onto the parser's lexer stack. */
cp_token_cache *tokens = DEFPARSE_TOKENS (condition);
cp_parser_push_lexer_for_tokens (parser, tokens);

/* if we have a current class object, constify it before processing
* the contract condition */
if (flag_contracts_nonattr
&& !flag_contracts_nonattr_noconst
&& current_class_ref)
current_class_ref = view_as_const (current_class_ref);
bool old_flag_contracts_nonattr_noconst = flag_contracts_nonattr_noconst;
bool should_constify = get_contract_const (contract);
/* If we have a current class object, see if we need to consider
it const when processing the contract condition. */
tree current_class_ref_copy = current_class_ref;
if (should_constify && current_class_ref_copy)
current_class_ref = view_as_const (current_class_ref_copy);

/* Parse the condition, ensuring that parameters or the return variable
aren't flagged for use outside the body of a function. */
Expand Down
8 changes: 1 addition & 7 deletions gcc/cp/pt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12047,15 +12047,9 @@ tsubst_contract (tree decl, tree t, tree args, tsubst_flags_t complain,

/* Instantiate the condition. If the return type is undeduced, process
the expression as if inside a template to avoid spurious type errors. */
bool mutable_p = get_contract_mutable (t);
bool const_p = get_contract_const (t);
bool old_flag_contracts_nonattr_noconst = flag_contracts_nonattr_noconst;
if (flag_contracts_nonattr && flag_contracts_nonattr_mutable_keyword &&
mutable_p)
flag_contracts_nonattr_noconst = 1;
if (flag_contracts_nonattr && flag_contracts_nonattr_const_keyword &&
!const_p)
flag_contracts_nonattr_noconst = 1;
flag_contracts_nonattr_noconst = !const_p;

if (auto_p)
++processing_template_decl;
Expand Down

0 comments on commit 996b8d8

Please sign in to comment.