-
Notifications
You must be signed in to change notification settings - Fork 0
Contracts p3097 #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3e5995e
18005a1
31abb0c
eea5805
04d452d
80e45c2
b2d866a
6ac6b2c
a1e4aef
7a77bf8
4737ee7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1512,7 +1512,8 @@ handle_contracts_p (tree decl1) | |
| { | ||
| return (flag_contracts | ||
| && !processing_template_decl | ||
| && DECL_ABSTRACT_ORIGIN (decl1) == NULL_TREE | ||
| && (DECL_ABSTRACT_ORIGIN (decl1) == NULL_TREE | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO we really need to disentangle this from the DECL_ABSTRACT_ORIGIN (apparent) mis-use - unless the documentation for that is out of date - we should check with the GCC C++ gurus .. ... if the use of DECL_ABSTRACT_ORIGIN is an (ab)-use then that should be fixed (but not part of this patch FAOD) |
||
| || DECL_IS_WRAPPER_FN_P(decl1)) | ||
| && contract_any_active_p (DECL_CONTRACTS (decl1))); | ||
| } | ||
|
|
||
|
|
@@ -2004,7 +2005,8 @@ start_function_contracts (tree decl1) | |
|
|
||
| /* Contracts may have just been added without a chance to parse them, though | ||
| we still need the PRE_FN available to generate a call to it. */ | ||
| if (!DECL_PRE_FN (decl1)) | ||
| /* Do we already have declarations generated ? */ | ||
| if (!DECL_PRE_FN (decl1) && !DECL_POST_FN (decl1)) | ||
| build_contract_function_decls (decl1); | ||
|
|
||
| } | ||
|
|
@@ -2191,7 +2193,7 @@ finish_function_contracts (tree fndecl) | |
| if (post) | ||
| { | ||
| DECL_PENDING_INLINE_P (post) = false; | ||
| start_preparsed_function (post, | ||
| start_preparsed_function (post, | ||
| DECL_ATTRIBUTES (post), | ||
| flags); | ||
| remap_and_emit_conditions (fndecl, post, POSTCONDITION_STMT); | ||
|
|
@@ -2201,6 +2203,12 @@ finish_function_contracts (tree fndecl) | |
| tree finished_post = finish_function (false); | ||
| expand_or_defer_fn (finished_post); | ||
| } | ||
|
|
||
| /* Check if we need to update wrapper function contracts. */ | ||
| tree wrapdecl = get_contract_wrapper_function (fndecl); | ||
| if (wrapdecl){ | ||
| copy_and_remap_contracts (wrapdecl, fndecl); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there some action needed if the wrapdecl is error_mark_node or absent? |
||
| } | ||
|
|
||
|
|
||
|
|
@@ -2330,7 +2338,7 @@ duplicate_contracts (tree newdecl, tree olddecl) | |
| PARM_DECL in OVERRIDER. */ | ||
|
|
||
| void | ||
| inherit_base_contracts (tree overrider, tree basefn) | ||
| copy_and_remap_contracts (tree overrider, tree basefn) | ||
| { | ||
| tree last = NULL_TREE, contract_attrs = NULL_TREE; | ||
| for (tree a = DECL_CONTRACTS (basefn); | ||
|
|
@@ -2357,4 +2365,224 @@ inherit_base_contracts (tree overrider, tree basefn) | |
| set_decl_contracts (overrider, contract_attrs); | ||
| } | ||
|
|
||
| /* Build a declaration for the contract wrapper of a caller FNDECL. */ | ||
|
|
||
| static tree | ||
| build_contract_wrapper_function (tree fndecl) | ||
| { | ||
| if (TREE_TYPE (fndecl) == error_mark_node) | ||
| return error_mark_node; | ||
|
|
||
| /* Use mangled name so we can differentiate between different virtual | ||
| functions of the same name. */ | ||
| const char *name = IDENTIFIER_POINTER ((DECL_ASSEMBLER_NAME (fndecl))); | ||
| name = ACONCAT ((name, ".contract_wrapper", NULL)); | ||
|
|
||
| location_t loc = DECL_SOURCE_LOCATION (fndecl); | ||
|
|
||
| tree wrapper_return_type = TREE_TYPE (TREE_TYPE (fndecl)); | ||
| tree wrapper_args = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); | ||
|
|
||
| tree wrapper_type = build_function_type ( wrapper_return_type, | ||
| wrapper_args); | ||
|
|
||
|
|
||
| /* this will create a member function because fndecl is a member function, | ||
| so we will need to adjust the type later */ | ||
| tree wrapdecl | ||
| = build_lang_decl_loc (loc, FUNCTION_DECL, get_identifier (name), wrapper_type); | ||
|
|
||
|
|
||
| DECL_CONTEXT (wrapdecl) = NULL_TREE; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we will add this later, I guess? |
||
| DECL_SOURCE_LOCATION (wrapdecl) = loc; | ||
| /*the declaration was implicitly generated by the compiler */ | ||
| DECL_ARTIFICIAL (wrapdecl) = true; | ||
| /* no function body at present * */ | ||
| DECL_INITIAL (wrapdecl) = error_mark_node; | ||
|
|
||
|
|
||
| if (DECL_RESULT (fndecl)) | ||
| { | ||
| DECL_RESULT (wrapdecl) = copy_decl (DECL_RESULT (fndecl)); | ||
| DECL_CONTEXT (DECL_RESULT (wrapdecl)) = wrapdecl; | ||
| } | ||
|
|
||
| /* Copy the function parameters. */ | ||
| tree last = DECL_ARGUMENTS (wrapdecl) = copy_decl (DECL_ARGUMENTS (fndecl)); | ||
| DECL_CONTEXT (last) = wrapdecl; | ||
| for (tree p = TREE_CHAIN (DECL_ARGUMENTS (fndecl)); p; p = TREE_CHAIN (p)) | ||
| { | ||
| if (VOID_TYPE_P (p)) | ||
| { | ||
| TREE_CHAIN (last) = void_list_node; | ||
| break; | ||
| } | ||
| last = TREE_CHAIN (last) = copy_decl (p); | ||
| DECL_CONTEXT (last) = wrapdecl; | ||
| } | ||
|
|
||
| /* Copy selected attributes from the original function. */ | ||
| TREE_USED (wrapdecl) = TREE_USED (fndecl); | ||
| if (DECL_SECTION_NAME (fndecl)) | ||
| set_decl_section_name (wrapdecl, fndecl); | ||
|
|
||
| /* Copy any alignment that the FE added. */ | ||
| if (DECL_ALIGN (fndecl)) | ||
| SET_DECL_ALIGN (wrapdecl, DECL_ALIGN (fndecl)); | ||
| /* Copy any alignment the user added. */ | ||
| DECL_USER_ALIGN (wrapdecl) = DECL_USER_ALIGN (fndecl); | ||
|
|
||
| /* Apply attributes from the original fn. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if any non-contracts attributes are relevant here? |
||
| TODO : we can probably skip this step if function needs | ||
| instantiating as it will be done as a part of instantiation. */ | ||
| copy_and_remap_contracts (wrapdecl, fndecl); | ||
|
|
||
| DECL_ABSTRACT_ORIGIN (wrapdecl) = fndecl; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still feel we're abusing this (according to the doc. - it should be on our TODO to establish if the abuse is real or the doc is out of date/insufficient) |
||
|
|
||
| /* Make this function internal */ | ||
| TREE_PUBLIC (wrapdecl) = false; | ||
| DECL_EXTERNAL (wrapdecl) = false; | ||
| DECL_WEAK (wrapdecl) = false; | ||
|
|
||
| // below is copied from build_contract_condition_function | ||
| DECL_INTERFACE_KNOWN (wrapdecl) = true; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what this does .. (not saying it's wrong, just not sure of the implications) |
||
|
|
||
| /* Update various inline related declaration properties. */ | ||
| //DECL_DECLARED_INLINE_P (wrapdecl) = true; | ||
| DECL_DISREGARD_INLINE_LIMITS (wrapdecl) = true; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not disagreeing with this - but note that it is a big hammer. |
||
| TREE_NO_WARNING (wrapdecl) = 1; | ||
|
|
||
| return wrapdecl; | ||
| } | ||
|
|
||
|
|
||
| /* Map from FUNCTION_DECL to a FUNCTION_DECL for contract wrapper. */ | ||
| static GTY(()) hash_map<tree, tree> *decl_wrapper_fn; | ||
|
|
||
| /* Returns the wrapper funtion for D, or null if not set. */ | ||
|
|
||
| tree | ||
| get_contract_wrapper_function (tree decl) | ||
| { | ||
| hash_map_maybe_create<hm_ggc> (decl_wrapper_fn); | ||
| tree *result = decl_wrapper_fn->get (decl); | ||
| return result ? *result : NULL_TREE; | ||
| } | ||
|
|
||
| /* Makes wrapper the precondition function for D. */ | ||
|
|
||
| void | ||
| set_contract_wrapper_function (tree decl, tree wrapper) | ||
| { | ||
| gcc_assert (wrapper); | ||
| hash_map_maybe_create<hm_ggc> (decl_wrapper_fn); | ||
| gcc_assert (!decl_wrapper_fn->get (decl)); | ||
| decl_wrapper_fn->put (decl, wrapper); | ||
| } | ||
|
|
||
|
|
||
| tree | ||
| maybe_contract_wrap_new_method_call (tree fndecl, tree call) | ||
| { | ||
|
|
||
| if (fndecl == error_mark_node) return fndecl; | ||
|
|
||
| /* We only need to wrap the function if it's a virtual function */ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like us (informally) to see how tricky it would be to have a flag that allows us to apply caller-side contracts generally - it would help (perhaps) with some of the SG21 + coroutines discussion. |
||
| if (!flag_contracts_nonattr || !handle_contracts_p (fndecl) | ||
| || !DECL_VIRTUAL_P (fndecl)) | ||
| return call; | ||
|
|
||
| bool do_pre = has_active_preconditions (fndecl); | ||
| bool do_post = has_active_postconditions (fndecl); | ||
|
|
||
| /* We should not have reached here with nothing to do... */ | ||
| gcc_checking_assert (do_pre || do_post); | ||
|
|
||
| /* build the declaration of the wrapper if we need to */ | ||
| tree wrapdecl = get_contract_wrapper_function (fndecl); | ||
|
|
||
| if (!wrapdecl){ | ||
| wrapdecl = build_contract_wrapper_function (fndecl); | ||
| wrapdecl = pushdecl_top_level (wrapdecl); | ||
| set_contract_wrapper_function (fndecl, wrapdecl); | ||
| } | ||
|
|
||
| unsigned nargs = call_expr_nargs (call); | ||
| vec<tree, va_gc> *argwrap; | ||
| vec_alloc (argwrap, nargs); | ||
|
|
||
| tree arg; | ||
| call_expr_arg_iterator iter; | ||
| FOR_EACH_CALL_EXPR_ARG (arg, iter, call) | ||
| { | ||
| argwrap->quick_push (arg); | ||
| } | ||
|
|
||
| tree wrapcall = build_call_expr_loc_vec(DECL_SOURCE_LOCATION (wrapdecl), wrapdecl, argwrap); | ||
|
|
||
| CALL_FROM_THUNK_P (wrapcall) = true; | ||
| return wrapcall; | ||
| } | ||
|
|
||
|
|
||
| bool define_contract_wrapper_func(const tree& fndecl, const tree& wrapdecl, void*) | ||
| { | ||
|
|
||
| start_preparsed_function (wrapdecl, /*DECL_ATTRIBUTES (wrapdecl)*/ NULL_TREE, SF_DEFAULT | SF_PRE_PARSED); | ||
|
|
||
| tree body = begin_function_body (); | ||
| tree compound_stmt = begin_compound_stmt (BCS_FN_BODY); | ||
|
|
||
|
|
||
| vec<tree, va_gc> * args = build_arg_list (wrapdecl); | ||
| tree return_type = TREE_TYPE (TREE_TYPE (fndecl)); | ||
| tree *class_ptr = args->begin(); | ||
| tree fn; | ||
| gcc_assert (class_ptr); | ||
|
|
||
| tree t; | ||
| tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (*class_ptr)), | ||
| DECL_CONTEXT (fndecl), | ||
| ba_any, NULL, tf_warning_or_error); | ||
| gcc_assert (binfo && binfo != error_mark_node); | ||
|
|
||
| *class_ptr = build_base_path (PLUS_EXPR, *class_ptr, binfo, 1, | ||
| tf_warning_or_error); | ||
| if (TREE_SIDE_EFFECTS (*class_ptr)) | ||
| *class_ptr = save_expr (*class_ptr); | ||
| t = build_pointer_type (TREE_TYPE (fndecl)); | ||
| fn = build_vfn_ref (*class_ptr, DECL_VINDEX (fndecl)); | ||
| TREE_TYPE (fn) = t; | ||
|
|
||
|
|
||
| tree call = build_call_a (fn, | ||
| args->length (), | ||
| args->address ()); | ||
|
|
||
| if (!VOID_TYPE_P (return_type)) | ||
| { | ||
| tree r = build_cplus_new(return_type, call, tf_warning_or_error); | ||
| finish_return_stmt (r); | ||
| } | ||
| else { | ||
| finish_return_stmt (NULL_TREE); | ||
| } | ||
| finish_compound_stmt (compound_stmt); | ||
| finish_function_body (body); | ||
| expand_or_defer_fn (finish_function (/*inline_p=*/false)); | ||
| return true; | ||
| } | ||
|
|
||
| void emit_contract_wrapper_func() | ||
| { | ||
| if (!decl_wrapper_fn || decl_wrapper_fn->is_empty ()) return; | ||
|
|
||
| decl_wrapper_fn->traverse<void *, define_contract_wrapper_func>(NULL); | ||
|
|
||
| decl_wrapper_fn->empty (); | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
| #include "gt-cp-contracts.h" | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why "new" method call and not just method_call or call ? (the hiccup here is that 'new' has some specific meaning additional to the "just done" one).