[PATCH] PING implement pre-c++20 contracts

Jason Merrill jason@redhat.com
Mon Jan 18 14:56:26 GMT 2021


On 1/4/21 9:58 AM, Jeff Chapman wrote:
> Ping. re: 
> https://gcc.gnu.org/pipermail/gcc-patches/2020-December/561135.html 
> <https://gcc.gnu.org/pipermail/gcc-patches/2020-December/561135.html>
> 
>      > OK, I'll start with -alt then, thanks.
> 
>     Andrew is exactly correct, contracts-jac-alt is still the current
>     branch we're focusing our upstreaming efforts on.
> 
>     It's trailing upstream master by a fair bit at this point. I'll get
>     a merge pushed shortly.
> 
> 
> The latest is still on the same branch, which hasn't been updated since 
> that last merge:
> https://github.com/lock3/gcc/tree/contracts-jac-alt 
> <https://github.com/lock3/gcc/tree/contracts-jac-alt>
> 
> Would you prefer me to keep it from trailing upstream too much through 
> regular merges, or would it be more beneficial for it to be left alone 
> so you have a more stable review target?
> 
> Please let me know if there's initial feedback I can start addressing, 
> or anything I can do to help the review process along in general.

Why is some of the code in c-family?  From the modules merge there is 
now a cp_handle_option function that you could add the option handling 
to, and I don't see a reason for cxx-contracts.c to be in c-family/ 
rather than cp/.

And then much of the code you add to decl.c could also move to the 
contracts file, and some of the contracts stuff in cp-tree.h could move 
to cxx-contracts.h?

> +extern bool cxx23_contract_attribute_p (const_tree);

This name seems optimistic.  :)
Let's call it cxx_contract_attribute_p.

> +/* Return TRUE iff ATTR has been parsed by the fornt-end as a c++2a contract

"front"

> @@ -566,7 +566,11 @@ decl_attributes (tree *node, tree attributes, int flags,
>         {
>           if (!(flags & (int) ATTR_FLAG_BUILT_IN))
>             {
> -             if (ns == NULL_TREE || !cxx11_attr_p)
> +             if (cxx23_contract_attribute_p (attr))
> +               {
> +                 ; /* Do not warn about contract "attributes".  */
> +               }

I don't want the language-independent code to have to know about this. 
If you want decl_attributes to ignore these attributes, you could give 
these attributes a dummy spec that just returns?

> +set_decl_contracts (tree decl, tree contract_attrs)
> +{
> +  remove_contract_attributes (decl);
> +  if (!DECL_ATTRIBUTES (decl))
> +    {
> +      DECL_ATTRIBUTES (decl) = contract_attrs;
> +      return;
> +    }
> +  tree last_attr = DECL_ATTRIBUTES (decl);
> +  while (TREE_CHAIN (last_attr))
> +    last_attr = TREE_CHAIN (last_attr);
> +  TREE_CHAIN (last_attr) = contract_attrs;

I think you want to use 'chainon' here.

> @@ -5498,10 +5863,17 @@ start_decl (const cp_declarator *declarator,
>  
>        if (DECL_EXTERNAL (decl) && ! DECL_TEMPLATE_SPECIALIZATION (decl)
>           /* Aliases are definitions. */
> -         && !alias)
> +         && !alias
> +         && (DECL_VIRTUAL_P (decl) || !flag_contracts))
>         permerror (declarator->id_loc,
>                    "declaration of %q#D outside of class is not definition",
>                    decl);
> +      else if (DECL_EXTERNAL (decl) && ! DECL_TEMPLATE_SPECIALIZATION (decl)
> +         /* Aliases are definitions. */
> +         && !alias
> +         && flag_contract_strict_declarations)
> +       warning_at (declarator->id_loc, OPT_fcontract_strict_declarations_,
> +                   "non-defining declaration of %q#D outside of class", decl);

Let's keep the same message for the two cases.

> +void
> +finish_function_contracts (tree fndecl, bool is_inline)

This function needs a comment.

> +/* cp_tree_defined_p helper -- returns TP if TP is undefined.  */
> +
> +static tree
> +cp_tree_defined_p_r (tree *tp, int *, void *)
> +{
> +  enum tree_code code = TREE_CODE (*tp);
> +  if ((code == FUNCTION_DECL || code == VAR_DECL)
> +      && !decl_defined_p (*tp))
> +    return *tp;
> +  /* We never want to accidentally instantiate templates.  */
> +  if (code == TEMPLATE_DECL)
> +    return *tp; /* FIXME? */

In what context are you getting a TEMPLATE_DECL here?  I don't see how 
this would have an effect on instantiations.

> +/* Parse a conditional-expression.  */
> +/* FIXME: should callers use cp_parser_constant_expression?  */
> +
> +static cp_expr
> +cp_parser_conditional_expression (cp_parser *parser)
...
> +  /* FIXME: can we use constant_expression for this?  */
> +  cp_expr cond = cp_parser_conditional_expression (parser);

I don't think we want to use cp_parser_constant_expression for 
expressions that are not intended to be constant.

> +  bool finishing_guarded_p = true//!processing_template_decl

?

> +    /* FIXME: Is this going to leak?  */
> +    comment_str = xstrdup (expr_to_string (cond));

There's no need to strdup here (and free a few lines later); 
build_string_literal copies the bytes.  The return value of 
expr_to_string is in GC memory.

> +  /* If we have contracts, check that they're valid in this context.  */
> +  // FIXME: These aren't entirely correct.

How not?  Can local extern function decls have contract attributes?

> +  if (tree pre = lookup_attribute ("pre", contract_attrs))
> +    error_at (EXPR_LOCATION (TREE_VALUE (pre)),
> +             "preconditions cannot be statements");
> +  else if (tree post = lookup_attribute ("post", contract_attrs))
> +    error_at (EXPR_LOCATION (TREE_VALUE (post)),
> +             "postconditions cannot be statements");
> +
> +  /* FIXME: move fallthrough up here so it applies to decls/etc?  */

That would make sense.

> +  temp_override<tree> saved_cct(current_class_type);
> +  temp_override<tree> saved_ccp(current_class_ptr);
> +  temp_override<tree> saved_ccr(current_class_ref);

Are these actually needed?

> +++ b/gcc/tree-inline.c
> @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "ssa.h"
>  #include "cgraph.h"
>  #include "tree-pretty-print.h"
> +#include "cp/cp-tree.h"

Can't include cp-tree.h in a language-independent file.  We already call 
the tree-inline machinery from the front-end in clone_body; can't you do 
the same for contracts?

> +build_arg_list (tree fn)
> +{
> +  gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
> +
> +  vec<tree, va_gc> *args = make_tree_vector ();
> +  for (tree t = DECL_ARGUMENTS (fn); t != NULL_TREE; t = TREE_CHAIN (t))
> +    {
> +      if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
> +         && DECL_NAME (t) != NULL_TREE
> +         && IDENTIFIER_POINTER (DECL_NAME (t)) != NULL
> +         && id_equal (DECL_NAME (t), "this"))

You can use is_this_parameter here.

> +             /* FIXME when we instatiate a template class with guarded
> +              * members, particularly guarded template members, the resulting
> +              * pre/post functions end up being inaccessible because their
> +              * template info's context is the original uninstantiated class.

This sounds like a significant functionality gap.  I'm guessing you want 
to reconsider the unshare_template approach.

> +// FIXME: are we sure we shouldn't just mangle or use some existing machinery?
> +static const char *
> +get_contracts_internal_decl_name (const char *ident)

Definitely use the mangling machinery.

> +      /* FIXME do we need magic to perfectly forward this so we don't clobber
> +        RVO/NRVO etc?  */

Yes.  CALL_FROM_THUNK_P will probably get you some of the magic you want.

More soon.

Jason



More information about the Gcc-patches mailing list