return condition_conversion (condition);
}
+/* Wrap the DECL into VIEW_CONVERT_EXPR representing const qualified version
+ of the declaration. */
+
+tree
+view_as_const (tree decl)
+{
+ if (!contract_const_wrapper_p (decl))
+ {
+ tree ctype = TREE_TYPE (decl);
+ location_t loc =
+ EXPR_P (decl) ? EXPR_LOCATION (decl) : DECL_SOURCE_LOCATION (decl);
+ ctype = cp_build_qualified_type (ctype, (cp_type_quals (ctype)
+ | TYPE_QUAL_CONST));
+ decl = build1 (VIEW_CONVERT_EXPR, ctype, decl);
+ SET_EXPR_LOCATION (decl, loc);
+ /* Mark the VCE as contract const wrapper. */
+ CONST_WRAPPER_P (decl) = true;
+ }
+ return decl;
+}
+
+/* Constify access to DECL from within the contract condition. */
+
+tree
+constify_contract_access (tree decl)
+{
+ /* We check if we have a variable, a parameter, a variable of reference type,
+ * or a parameter of reference type
+ */
+ if (!TREE_READONLY (decl)
+ && (VAR_P (decl)
+ || (TREE_CODE (decl) == PARM_DECL)
+ || (REFERENCE_REF_P (decl)
+ && (VAR_P (TREE_OPERAND (decl, 0))
+ || (TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL)
+ || (TREE_CODE (TREE_OPERAND (decl, 0))
+ == TEMPLATE_PARM_INDEX)))))
+ decl = view_as_const (decl);
+
+ return decl;
+}
+
+/* Indicate that PARM_DECL DECL is ODR used in a postcondition. */
+
+static void
+set_parm_used_in_post (tree decl, bool constify = true)
+{
+ gcc_checking_assert (TREE_CODE (decl) == PARM_DECL);
+ DECL_LANG_FLAG_4 (decl) = constify;
+}
+
+/* Test if PARM_DECL is ODR used in a postcondition. */
+
+static bool
+parm_used_in_post_p (const_tree decl)
+{
+ /* Check if this parameter is odr used within a function's postcondition */
+ return ((TREE_CODE (decl) == PARM_DECL) && DECL_LANG_FLAG_4 (decl));
+}
+
+/* If declaration DECL is a PARM_DECL and it appears in a postcondition, then
+ check that it is not a non-const by-value param. LOCATION is where the
+ expression was found and is used for diagnostic purposes. */
+
+void
+check_param_in_postcondition (tree decl, location_t location)
+{
+ if (processing_postcondition
+ && TREE_CODE (decl) == PARM_DECL
+ /* TREE_CODE (decl) == PARM_DECL only holds for non-reference
+ parameters. */
+ && !cp_unevaluated_operand
+ /* Return value parameter has DECL_ARTIFICIAL flag set. The flag
+ presence of the flag should be sufficient to distinguish the
+ return value parameter in this context. */
+ && !(DECL_ARTIFICIAL (decl)))
+ {
+ set_parm_used_in_post (decl);
+
+ if (!dependent_type_p (TREE_TYPE (decl))
+ && !CP_TYPE_CONST_P (TREE_TYPE (decl)))
+ {
+ error_at (location,
+ "a value parameter used in a postcondition must be const");
+ inform (DECL_SOURCE_LOCATION (decl), "parameter declared here");
+ }
+ }
+}
+
+/* Check if parameters used in postconditions are const qualified on
+ a redeclaration that does not specify contracts or on an instantiation
+ of a function template. */
+
+void
+check_postconditions_in_redecl (tree olddecl, tree newdecl)
+{
+ tree contract_spec = get_fn_contract_specifiers (olddecl);
+ if (!contract_spec)
+ return;
+
+ tree t1 = FUNCTION_FIRST_USER_PARM (olddecl);
+ tree t2 = FUNCTION_FIRST_USER_PARM (newdecl);
+
+ for (; t1 && t1 != void_list_node;
+ t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
+ {
+ if (parm_used_in_post_p (t1))
+ {
+ set_parm_used_in_post (t2);
+ if (!dependent_type_p (TREE_TYPE (t2))
+ && !CP_TYPE_CONST_P (TREE_TYPE (t2))
+ && !TREE_READONLY (t2))
+ {
+ error_at (DECL_SOURCE_LOCATION (t2),
+ "value parameter %qE used in a postcondition must be const", t2);
+ inform (DECL_SOURCE_LOCATION (olddecl),
+ "previous declaration here");
+ }
+ }
+ }
+}
+
void
maybe_update_postconditions (tree fndecl)
{
}
if (old_contracts && !new_contracts)
- return;
+ /* We allow re-declarations to omit contracts declared on the initial decl.
+ In fact, this is required if the conditions contain lambdas. Check if
+ all the parameters are correctly const qualified. */
+ check_postconditions_in_redecl (olddecl, newdecl);
else if (old_contracts && new_contracts
&& !contract_any_deferred_p (old_contracts)
&& contract_any_deferred_p (new_contracts)
extern void check_redecl_contract (tree, tree);
extern tree invalidate_contract (tree);
extern tree copy_and_remap_contracts (tree, tree);
+extern tree constify_contract_access (tree);
+extern tree view_as_const (tree);
extern void set_fn_contract_specifiers (tree, tree);
extern void update_fn_contract_specifiers (tree, tree);
set_fn_contract_specifiers (decl, contract_attrs);
}
+/* Test if EXP is a contract const wrapper node. */
+
+inline bool
+contract_const_wrapper_p (const_tree exp)
+{
+ /* A wrapper node has code VIEW_CONVERT_EXPR, and the flag base.private_flag
+ is set. The wrapper node is used to used to constify entities inside
+ contract assertions. */
+ return ((TREE_CODE (exp) == VIEW_CONVERT_EXPR) && CONST_WRAPPER_P (exp));
+}
+
+/* If EXP is a contract_const_wrapper_p, return the wrapped expression.
+ Otherwise, do nothing. */
+
+inline tree
+strip_contract_const_wrapper (tree exp)
+{
+ if (contract_const_wrapper_p (exp))
+ return TREE_OPERAND (exp, 0);
+ else
+ return exp;
+}
+
/* TODO : decide if we should push the tests into contracts.cc */
extern contract_evaluation_semantic get_evaluation_semantic (const_tree);
DECL_SELF_REFERENCE_P (in a TYPE_DECL)
DECL_INVALID_OVERRIDER_P (in a FUNCTION_DECL)
DECL_UNINSTANIATED_TEMPLATE_FRIEND_P (in TEMPLATE_DECL)
+ parm_used_in_post_p (in PARM_DECL)
5: DECL_INTERFACE_KNOWN.
6: DECL_THIS_STATIC (in VAR_DECL, FUNCTION_DECL or PARM_DECL)
DECL_FIELD_IS_BASE (in FIELD_DECL)
#include "target.h"
#include "decl.h"
#include "flags.h"
+#include "contracts.h"
/* Constructor for a lambda expression. */
init = PACK_EXPANSION_PATTERN (init);
}
+ init = strip_contract_const_wrapper (init);
+
if (INDIRECT_REF_P (init))
init = TREE_OPERAND (init, 0);
STRIP_NOPS (init);
cp_token_cache *tokens = DEFPARSE_TOKENS (condition);
cp_parser_push_lexer_for_tokens (parser, tokens);
+ /* If we have a current class object, we need to consider
+ it const when processing the contract condition. */
+ tree current_class_ref_copy = current_class_ref;
+ if (flag_contracts && 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. */
begin_scope (sk_contract, fn);
/* Enable location wrappers when parsing contracts. */
auto suppression = make_temp_override (suppress_location_wrappers, 0);
+ /* 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 (current_class_ref_copy)
+ current_class_ref = view_as_const (current_class_ref_copy);
+
/* Parse the condition. */
begin_scope (sk_contract, current_function_decl);
bool old_pc = processing_postcondition;
processing_postcondition = old_pc;
pop_bindings_and_leave_scope ();
+ /* Revert (any) constification of the current class object. */
+ current_class_ref = current_class_ref_copy;
+
parens.require_close (parser);
if (!contract || contract == error_mark_node)
/* Enable location wrappers when parsing contracts. */
auto suppression = make_temp_override (suppress_location_wrappers, 0);
+ /* 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 (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. */
begin_scope (sk_contract, current_function_decl);
&& scope_chain->bindings->kind == sk_contract);
pop_bindings_and_leave_scope ();
+ /* Revert (any) constification of the current class object. */
+ current_class_ref = current_class_ref_copy;
+
if (contract != error_mark_node)
{
location_t end = cp_lexer_peek_token (parser->lexer)->location;
if (tree ctrct = get_fn_contract_specifiers (t))
set_fn_contract_specifiers (r, ctrct);
+ /* The parms have now been substituted, check for incorrect const cases. */
+ check_postconditions_in_redecl (t, r);
+
if (DECL_FRIEND_CONTEXT (t))
SET_DECL_FRIEND_CONTEXT (r,
tsubst (DECL_FRIEND_CONTEXT (t),
/* force_paren_expr can also create a VIEW_CONVERT_EXPR. */
RETURN (finish_parenthesized_expr (op));
+ check_param_in_postcondition (op, EXPR_LOCATION (t));
+
+ if (flag_contracts && processing_contract_condition)
+ op = constify_contract_access (op);
+
/* Otherwise, we're dealing with a wrapper to make a C++20 template
parameter object const. */
if (TREE_TYPE (op) == NULL_TREE
"integral or enumeration type", decl, TREE_TYPE (decl));
*non_integral_constant_expression_p = true;
}
+
+ if (flag_contracts && processing_contract_condition)
+ r = constify_contract_access (r);
+
return r;
}
else if (TREE_CODE (decl) == UNBOUND_CLASS_TEMPLATE)
}
}
+ check_param_in_postcondition (decl, location);
+ if (flag_contracts && processing_contract_condition)
+ decl = constify_contract_access (decl);
+
return cp_expr (decl, location);
}
if (identifier_p (expr))
expr = lookup_name (expr);
+ /* If e is a constified expression inside a contract assertion,
+ strip the const wrapper. Per P2900R14, "For a function f with the
+ return type T , the result name is an lvalue of type const T , decltype(r)
+ is T , and decltype((r)) is const T&." */
+ expr = strip_contract_const_wrapper (expr);
+
if (INDIRECT_REF_P (expr)
|| TREE_CODE (expr) == VIEW_CONVERT_EXPR)
/* This can happen when the expression is, e.g., "a.b". Just
ENUM_IS_OPAQUE in
ENUMERAL_TYPE
+ CONST_WRAPPER_P in
+ VIEW_CONVERT_EXPR (used by C++)
+
protected_flag:
TREE_PROTECTED in
/* Determines whether an ENUMERAL_TYPE has defined the list of constants. */
#define ENUM_IS_OPAQUE(NODE) (ENUMERAL_TYPE_CHECK (NODE)->base.private_flag)
+/* Determines whether a VIEW_CONVERT_EXPR node is used to create const
+ qualified variant of its first operand (used by C++ contracts). */
+#define CONST_WRAPPER_P(NODE) \
+ (TREE_CHECK (NODE, VIEW_CONVERT_EXPR)->base.private_flag)
+
/* In an expr node (usually a conversion) this means the node was made
implicitly and should not lead to any sort of warning. In a decl node,
warnings concerning the decl should be suppressed. This is used at