On 5/14/21 4:54 PM, Jason Merrill wrote:
On 4/30/21 1:44 PM, Jeff Chapman wrote:
Hello! Looping back around to this. re:
https://gcc.gnu.org/pipermail/gcc-patches/2021-March/567334.html
On 3/25/21, Jason Merrill <ja...@redhat.com> wrote:
On 3/1/21 8:12 AM, Jeff Chapman wrote:
On 1/18/21, Jason Merrill <ja...@redhat.com> wrote:
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>
https://github.com/lock3/gcc/tree/contracts-jac-alt
<https://github.com/lock3/gcc/tree/contracts-jac-alt>
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/.
I've been pushing changes that address the points raised and wanted to
ping to see if there's more feedback and give a status summary. The
notable change is making sure the implementation plays nicely with
modules and a mangling change that did away with a good chunk of code.
The contracts code outside of cp/ has been moved into it, and the
contract attributes have been altered to work with language independent
handling code. More of the contracts code can probably still be
moved to
cxx-contracts which I'll loop back to after other refactoring. The
naming, spelling, and comments (or lack thereof) have been addressed.
Sounds good. I plan to get back to this when GCC 11 branches, which
should be mid-April.
Please let me know if you see any more issues when you pick it back up.
Particularly in modules interop, since I don't think you've had a chance
to look at that yet.
Completed another merge with master earlier this week which didn't bring
to light any new issues or regressions, but I'll keep on that :)
+ /* 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?
+ /* 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.
One approach would be to only do the pre/post/guarded/unguarded
transformation for a fully-instantiated function; a temploid function
would leave the contracts as attributes.
+ /* 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.
These points are still being investigated and addressed; including them
for reference.
Any update?
More soon.
Please let me know what other issues need work.
If there's anything I can do to make the process smoother please don't
hesitate to ask.
Larger-scope comments:
Please add an overview of the implementation strategy to the top of
cxx-contracts.c. Particularly to discuss the why and how of
pre/post/guarded/unguarded functions.
I'm confused by the approach to late parsing of contracts; it seems like
you wait until the end of parsing the function to go back and parse the
contracts. Why not parse them sooner, along with nsdmis, noexcept, and
function bodies?
Smaller-scope comments:
+ /* If we didn't build a check, insert a NOP so we don't leak
+ contracts into GENERIC. */
+ *stmt_p = build1 (NOP_EXPR, void_type_node, integer_zero_node);
You can use void_node for the NOP.
+ error_at (EXPR_LOCATION (new_contract),
+ "mismatched contract condition in %s",
+ ctx == cmc_declaration ? "declaration" : "override");
This sort of build-up of diagnostic messages by substring replacement
doesn't work very well for translations. In general, don't use %s for
inserting English text, only code strings that will be the same in all
languages.
+ /* Remove the associated contracts and unchecked result, if any. */
+ if (flag_contracts && TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ remove_contract_attributes (newdecl);
+ set_contract_functions (newdecl, NULL_TREE, NULL_TREE);
+ }
Why bother removing attributes on a decl that's about to be freed?
Why did we set the contract functions above only to clear them now?
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_,
+ "declaration of %q#D outside of class is not
definition",
+ decl);
Let's share the common predicates.
+ /* FIXME: move fallthrough up here so it applies to decls/etc? */
That seems unnecessary, we already warn and ignore fallthrough on a decl.
+int cp_contract_operand = 0;
+tree cp_contract_return_value = NULL_TREE;
These need to be saved/restored by push_to/pop_from_top_level somehow,
so that they don't stay set when parsing the contract triggers template
instantiation. The best way is probably to add them to struct saved_scope.
+ token = cp_lexer_peek_token (parser->lexer);
+ if (token->type == CPP_NAME)
+ {
+ attr_name = token->u.value;
+ attr_name = canonicalize_attr_name (attr_name);
+ }
+
+ /* Handle contract-attribute-specs specially. */
+ if (attr_name && contract_attribute_p (attr_name))
+ {
+ attributes
+ = cp_parser_contract_attribute_spec (parser, attr_name);
+ goto finish_attrs;
+ }
Let's move the name checking inside cp_parser_contract_attribute_spec,
and make that function return null for a non-contract attribute.
+ /* FIXME we can do this a lot more efficiently? Once per function
for all of
+ * its pre contracts, and then once per post contract? Is there an
+ * appreciable difference? Or a way to simply rename the post ret
val parm? *
You might want to share the context handling with late parsing of noexcept.
+ /* Don't do access checking if it is a templated function. */
+ if (processing_template_decl)
+ push_deferring_access_checks (dk_no_check);
Don't we already defer access checking for templated functions?
+/* Return a copy of the FUNCTION_DECL IDECL with its own unshared +
PARM_DECL and DECL_ATTRIBUTEs. */
+
+static tree
+copy_fn_decl (tree idecl)
Can you use, or at least share code with, the existing
copy_fndecl_with_name?
+ /* FIXME will later optimizations delete unused args to prevent
extra arg
+ * passing? do we care? */
It might be possible to leverage the optimizer's partial-inlining code
for this, I don't know. Probably not worth messing with now.
More soon.
More:
+ if (semantic == CCS_ASSUME && !cp_tree_defined_p (c))
+ break;
Another approach to this would be to evaluate an assume with a separate
non_constant_p and within the lifetime of a
uid_sensitive_constexpr_evaluation_sentinel. But the way you have it is
fine.
+ /* Ignored contracts are never checked or assumed. */
+ if (semantic == CCS_IGNORE)
+ return build1 (NOP_EXPR, void_type_node, integer_zero_node);
Another place you can use void_node.
+static void
+build_contract_handler_fn (tree contract,
+ contract_continuation cmode)
This function needs a comment, and a better name. Maybe just
build_contract_handler_fn_call.
+/* Rewrite the post function decl of FNDECL, replacing the original undeduced
+ return type with RETURN_TYPE. */
+
+static void
+apply_post_deduced_return_type (tree fndecl, tree return_type)
Can this share more code with apply_deduced_return_type?
+ if (needs_post && DECL_POST_FN (current_function_decl) != error_mark_node)
+ {
+ vec<tree, va_gc> *args = build_arg_list (current_function_decl);
+ if (DECL_UNCHECKED_RESULT (current_function_decl))
+ vec_safe_push (args, expr); // FIXME do we need forward_parm or similar?
+
+ if (undeduced_auto_decl (DECL_POST_FN (current_function_decl)))
+ apply_post_deduced_return_type (current_function_decl,
+ TREE_TYPE (expr));
+
+ push_deferring_access_checks (dk_no_check);
+ tree call = finish_call_expr (DECL_POST_FN (current_function_decl),
&args,
+ /*disallow_virtual=*/true,
+ /*koenig_p=*/false,
+ /*complain=*/tf_warning_or_error);
+ gcc_assert (call != error_mark_node);
+ pop_deferring_access_checks ();
+
+ /* Replace returned expression with call to post function. */
+ expr = call;
+ }
This looks like you pass the returned expression as an argument to the
post function instead of using it to initialize the return value. That
will break copy elision.
What if instead (for a class return type at least) you initialize the
return value, then pass a reference to the return value to the post
function? Then you could also share the call to the post function
between all returns.
+ if (tree check = build_contract_check (stmt))
+ {
+ /* Mark the current function as possibly throwing exceptions
+ (through invocation of the contract violation handler). */
+ current_function_returns_abnormally = 1;
+ TREE_NOTHROW (current_function_decl) = 0;
These shouldn't be set here, they should be set as part of building the
call to the handler. You should get that by changing build_call_expr to
build_call_n...
+ tree call = build_call_expr (violation_fn, 8, continue_mode, line_number,
+ file_name, function_name, comment,
+ level_str, role_str,
+ continuation);
...here.
+ /* FIXME: It looks like we have two bits of information for
+ continuing. Is this right? */
I think this is no longer true since you dropped "always continue".
+ char buf[1024 + 4]{};
+ char *res = buf;
+ size_t len = 1024;
This seems like a good place to use obstacks.
+ int s = expstart.column - 1;
+ int l = expend.column - expstart.column + 1;
So, l = expend.column - s?
+++ b/libstdc++-v3/src/c++17/contract.cc
Hmm. Contracts library support was in clause 17, which would mean this
should go in libstdc++-v3/libsupc++ instead. But it depends on
string_view, which is not in clause 17. This seems like a problem in
the standardese.
+ /* If this is the pre/post function for a guarded function, append
+ pre/post in the vendor specific portion of the mangling.
+
+ TODO: this likely needs standardizing.
+ TODO: do we need special handling in other tools like the demangler? */
+ if (DECL_IS_PRE_FN_P (decl))
+ write_string (".pre");
+ else if (DECL_IS_POST_FN_P (decl))
+ write_string (".post");
Do you have in mind that these functions are part of the ABI, or
internal implementation details of the functions? If the former, we
need to give them a proper mangling. If the latter, we can make them
hidden symbols in the same COMDAT section with their guarded function
like with constructor variants, and their mangling isn't important. I'm
still wondering why you split them out from the guarded function in the
first place.
+ remap_overrider_contracts (overrider, basefn);
+ remap_overrider_contracts (DECL_PRE_FN (overrider), DECL_PRE_FN
(basefn));
+ remap_overrider_contracts (DECL_POST_FN (overrider), DECL_POST_FN
(basefn));
Why three times? Why copy the contracts from the guarded function to
the pre/post functions?
+/* Assertion role info. */
This needs elaboration about what a role is.
A lot of the code in decl.c could move to cxx-contracts.c.
Jason