[C++ coroutines] Initial implementation.
This is the squashed version of the first 6 patches that were split to facilitate review. The changes to libiberty (7th patch) to support demangling the co_await operator stand alone and are applied separately. The patch series is an initial implementation of a coroutine feature, expected to be standardised in C++20. Standardisation status (and potential impact on this implementation) -------------------------------------------------------------------- The facility was accepted into the working draft for C++20 by WG21 in February 2019. During following WG21 meetings, design and national body comments have been reviewed, with no significant change resulting. The current GCC implementation is against n4835 [1]. At this stage, the remaining potential for change comes from: * Areas of national body comments that were not resolved in the version we have worked to: (a) handling of the situation where aligned allocation is available. (b) handling of the situation where a user wants coroutines, but does not want exceptions (e.g. a GPU). * Agreed changes that have not yet been worded in a draft standard that we have worked to. It is not expected that the resolution to these can produce any major change at this phase of the standardisation process. Such changes should be limited to the coroutine-specific code. ABI --- The various compiler developers 'vendors' have discussed a minimal ABI to allow one implementation to call coroutines compiled by another. This amounts to: 1. The layout of a public portion of the coroutine frame. Coroutines need to preserve state across suspension points, the storage for this is called a "coroutine frame". The ABI mandates that pointers into the coroutine frame point to an area begining with two function pointers (to the resume and destroy functions described below); these are immediately followed by the "promise object" described in the standard. This is sufficient that the builtins can take a coroutine frame pointer and determine the address of the promise (or call the resume/destroy functions). 2. A number of compiler builtins that the standard library might use. These are implemented by this patch series. 3. This introduces a new operator 'co_await' the mangling for which is also agreed between vendors (and has an issue filed for that against the upstream c++abi). Demangling for this is added to libiberty in a separate patch. The ABI has currently no target-specific content (a given psABI might elect to mandate alignment, but the common ABI does not do this). Standard Library impact ----------------------- The current implementations require addition of only a single header to the standard library (no change to the runtime). This header is part of the patch. GCC Implementation outline -------------------------- The standard's design for coroutines does not decorate the definition of a coroutine in any way, so that a function is only known to be a coroutine when one of the keywords (co_await, co_yield, co_return) is encountered. This means that we cannot special-case such functions from the outset, but must process them differently when they are finalised - which we do from "finish_function ()". At a high level, this design of coroutine produces four pieces from the original user's function: 1. A coroutine state frame (taking the logical place of the activation record for a regular function). One item stored in that state is the index of the current suspend point. 2. A "ramp" function This is what the user calls to construct the coroutine frame and start the coroutine execution. This will return some object representing the coroutine's eventual return value (or means to continue it when it it suspended). 3. A "resume" function. This is what gets called when a the coroutine is resumed when suspended. 4. A "destroy" function. This is what gets called when the coroutine state should be destroyed and its memory released. The standard's coroutines involve cooperation of the user's authored function with a provided "promise" class, which includes mandatory methods for handling the state transitions and providing output values. Most realistic coroutines will also have one or more 'awaiter' classes that implement the user's actions for each suspend point. As we parse (or during template expansion) the types of the promise and awaiter classes become known, and can then be verified against the signatures expected by the standard. Once the function is parsed (and templates expanded) we are able to make the transformation into the four pieces noted above. The implementation here takes the approach of a series of AST transforms. The state machine suspend points are encoded in three internal functions (one of which represents an exit from scope without cleanups). These three IFNs are lowered early in the middle end, such that the majority of GCC's optimisers can be run on the resulting output. As a design choice, we have carried out the outlining of the user's function in the front end, and taken advantage of the existing middle end's abilities to inline and DCE where that is profitable. Since the state machine is actually common to both resumer and destroyer functions, we make only a single function "actor" that contains both the resume and destroy paths. The destroy function is represented by a small stub that sets a value to signal the use of the destroy path and calls the actor. The idea is that optimisation of the state machine need only be done once - and then the resume and destroy paths can be identified allowing the middle end's inline and DCE machinery to optimise as profitable as noted above. The middle end components for this implementation are: A pass that: 1. Lowers the coroutine builtins that allow the standard library header to interact with the coroutine frame (these fairly simple logical or numerical substitution of values, given a coroutine frame pointer). 2. Lowers the IFN that represents the exit from state without cleanup. Essentially, this becomes a gimple goto. 3. Sets the final size of the coroutine frame at this stage. A second pass (that requires the revised CFG that results from the lowering of the scope exit IFNs in the first). 1. Lower the IFNs that represent the state machine paths for the resume and destroy cases. Patches squashed into this commit: [C++ coroutines 1] Common code and base definitions. This part of the patch series provides the gating flag, the keywords, cpp defines etc. [C++ coroutines 2] Define builtins and internal functions. This part of the patch series provides the builtin functions used by the standard library code and the internal functions used to implement lowering of the coroutine state machine. [C++ coroutines 3] Front end parsing and transforms. There are two parts to this. 1. Parsing, template instantiation and diagnostics for the standard- mandated class entries. The user authors a function that becomes a coroutine (lazily) by making use of any of the co_await, co_yield or co_return keywords. Unlike a regular function, where the activation record is placed on the stack, and is destroyed on function exit, a coroutine has some state that persists between calls - the 'coroutine frame' (thus analogous to a stack frame). We transform the user's function into three pieces: 1. A so-called ramp function, that establishes the coroutine frame and begins execution of the coroutine. 2. An actor function that contains the state machine corresponding to the user's suspend/resume structure. 3. A stub function that calls the actor function in 'destroy' mode. The actor function is executed: * from "resume point 0" by the ramp. * from resume point N ( > 0 ) for handle.resume() calls. * from the destroy stub for destroy point N for handle.destroy() calls. The C++ coroutine design described in the standard makes use of some helper methods that are authored in a so-called "promise" class provided by the user. At parse time (or post substitution) the type of the coroutine promise will be determined. At that point, we can look up the required promise class methods and issue diagnostics if they are missing or incorrect. To avoid repeating these actions at code-gen time, we make use of temporary 'proxy' variables for the coroutine handle and the promise - which will eventually be instantiated in the coroutine frame. Each of the keywords will expand to a code sequence (although co_yield is just syntactic sugar for a co_await). We defer the analysis and transformatin until template expansion is complete so that we have complete types at that time. 2. AST analysis and transformation which performs the code-gen for the outlined state machine. The entry point here is morph_fn_to_coro () which is called from finish_function () when we have completed any template expansion. This is preceded by helper functions that implement the phases below. The process proceeds in four phases. A Initial framing. The user's function body is wrapped in the initial and final suspend points and we begin building the coroutine frame. We build empty decls for the actor and destroyer functions at this time too. When exceptions are enabled, the user's function body will also be wrapped in a try-catch block with the catch invoking the promise class 'unhandled_exception' method. B Analysis. The user's function body is analysed to determine the suspend points, if any, and to capture local variables that might persist across such suspensions. In most cases, it is not necessary to capture compiler temporaries, since the tree-lowering nests the suspensions correctly. However, in the case of a captured reference, there is a lifetime extension to the end of the full expression - which can mean across a suspend point in which case it must be promoted to a frame variable. At the conclusion of analysis, we have a conservative frame layout and maps of the local variables to their frame entry points. C Build the ramp function. Carry out the allocation for the coroutine frame (NOTE; the actual size computation is deferred until late in the middle end to allow for future optimisations that will be allowed to elide unused frame entries). We build the return object. D Build and expand the actor and destroyer function bodies. The destroyer is a trivial shim that sets a bit to indicate that the destroy dispatcher should be used and then calls into the actor. The actor function is the implementation of the user's state machine. The current suspend point is noted in an index. Each suspend point is encoded as a pair of internal functions, one in the relevant dispatcher, and one representing the suspend point. During this process, the user's local variables and the proxies for the self-handle and the promise class instanceare re-written to their coroutine frame equivalents. The complete bodies for the ramp, actor and destroy function are passed back to finish_function for folding and gimplification. [C++ coroutines 4] Middle end expanders and transforms. The first part of this is a pass that provides: * expansion of the library support builtins, these are simple boolean or numerical substitutions. * The functionality of implementing an exit from scope without cleanup is performed here by lowering an IFN to a gimple goto. This pass has to run for non-coroutine functions, since functions calling the builtins are not necessarily coroutines (i.e. they are implementing the library interfaces which may be called from anywhere). The second part is the expansion of the coroutine IFNs that describe the state machine connections to the dispatchers. This only has to be run for functions that are coroutine components. The work done by this pass is: In the front end we construct a single actor function that contains the coroutine state machine. The actor function has three entry conditions: 1. from the ramp, resume point 0 - to initial-suspend. 2. when resume () is executed (resume point N). 3. from the destroy () shim when that is executed. The actor function begins with two dispatchers; one for resume and one for destroy (where the initial entry from the ramp is a special- case of resume point 0). Each suspend point and each dispatch entry is marked with an IFN such that we can connect the relevant dispatchers to their target labels. So, if we have: CO_YIELD (NUM, FINAL, RES_LAB, DEST_LAB, FRAME_PTR) This is await point NUM, and is the final await if FINAL is non-zero. The resume point is RES_LAB, and the destroy point is DEST_LAB. We expect to find a CO_ACTOR (NUM) in the resume dispatcher and a CO_ACTOR (NUM+1) in the destroy dispatcher. Initially, the intent of keeping the resume and destroy paths together is that the conditionals controlling them are identical, and thus there would be duplication of any optimisation of those paths if the split were earlier. Subsequent inlining of the actor (and DCE) is then able to extract the resume and destroy paths as separate functions if that is found profitable by the optimisers. Once we have remade the connections to their correct postions, we elide the labels that the front end inserted. [C++ coroutines 5] Standard library header. This provides the interfaces mandated by the standard and implements the interaction with the coroutine frame by means of inline use of builtins expanded at compile-time. There should be a 1:1 correspondence with the standard sections which are cross-referenced. There is no runtime content. At this stage, we have the content in an inline namespace "__n4835" for the CD we worked to. [C++ coroutines 6] Testsuite. There are two categories of test: 1. Checks for correctly formed source code and the error reporting. 2. Checks for transformation and code-gen. The second set are run as 'torture' tests for the standard options set, including LTO. These are also intentionally run with no options provided (from the coroutines.exp script). gcc/ChangeLog: 2020-01-18 Iain Sandoe <iain@sandoe.co.uk> * Makefile.in: Add coroutine-passes.o. * builtin-types.def (BT_CONST_SIZE): New. (BT_FN_BOOL_PTR): New. (BT_FN_PTR_PTR_CONST_SIZE_BOOL): New. * builtins.def (DEF_COROUTINE_BUILTIN): New. * coroutine-builtins.def: New file. * coroutine-passes.cc: New file. * function.h (struct GTY function): Add a bit to indicate that the function is a coroutine component. * internal-fn.c (expand_CO_FRAME): New. (expand_CO_YIELD): New. (expand_CO_SUSPN): New. (expand_CO_ACTOR): New. * internal-fn.def (CO_ACTOR): New. (CO_YIELD): New. (CO_SUSPN): New. (CO_FRAME): New. * passes.def: Add pass_coroutine_lower_builtins, pass_coroutine_early_expand_ifns. * tree-pass.h (make_pass_coroutine_lower_builtins): New. (make_pass_coroutine_early_expand_ifns): New. * doc/invoke.texi: Document the fcoroutines command line switch. gcc/c-family/ChangeLog: 2020-01-18 Iain Sandoe <iain@sandoe.co.uk> * c-common.c (co_await, co_yield, co_return): New. * c-common.h (RID_CO_AWAIT, RID_CO_YIELD, RID_CO_RETURN): New enumeration values. (D_CXX_COROUTINES): Bit to identify coroutines are active. (D_CXX_COROUTINES_FLAGS): Guard for coroutine keywords. * c-cppbuiltin.c (__cpp_coroutines): New cpp define. * c.opt (fcoroutines): New command-line switch. gcc/cp/ChangeLog: 2020-01-18 Iain Sandoe <iain@sandoe.co.uk> * Make-lang.in: Add coroutines.o. * cp-tree.h (lang_decl-fn): coroutine_p, new bit. (DECL_COROUTINE_P): New. * lex.c (init_reswords): Enable keywords when the coroutine flag is set, * operators.def (co_await): New operator. * call.c (add_builtin_candidates): Handle CO_AWAIT_EXPR. (op_error): Likewise. (build_new_op_1): Likewise. (build_new_function_call): Validate coroutine builtin arguments. * constexpr.c (potential_constant_expression_1): Handle CO_AWAIT_EXPR, CO_YIELD_EXPR, CO_RETURN_EXPR. * coroutines.cc: New file. * cp-objcp-common.c (cp_common_init_ts): Add CO_AWAIT_EXPR, CO_YIELD_EXPR, CO_RETRN_EXPR as TS expressions. * cp-tree.def (CO_AWAIT_EXPR, CO_YIELD_EXPR, (CO_RETURN_EXPR): New. * cp-tree.h (coro_validate_builtin_call): New. * decl.c (emit_coro_helper): New. (finish_function): Handle the case when a function is found to be a coroutine, perform the outlining and emit the outlined functions. Set a bit to signal that this is a coroutine component. * parser.c (enum required_token): New enumeration RT_CO_YIELD. (cp_parser_unary_expression): Handle co_await. (cp_parser_assignment_expression): Handle co_yield. (cp_parser_statement): Handle RID_CO_RETURN. (cp_parser_jump_statement): Handle co_return. (cp_parser_operator): Handle co_await operator. (cp_parser_yield_expression): New. (cp_parser_required_error): Handle RT_CO_YIELD. * pt.c (tsubst_copy): Handle CO_AWAIT_EXPR. (tsubst_expr): Handle CO_AWAIT_EXPR, CO_YIELD_EXPR and CO_RETURN_EXPRs. * tree.c (cp_walk_subtrees): Likewise. libstdc++-v3/ChangeLog: 2020-01-18 Iain Sandoe <iain@sandoe.co.uk> * include/Makefile.am: Add coroutine to the std set. * include/Makefile.in: Regenerated. * include/std/coroutine: New file. gcc/testsuite/ChangeLog: 2020-01-18 Iain Sandoe <iain@sandoe.co.uk> * g++.dg/coroutines/co-await-syntax-00-needs-expr.C: New test. * g++.dg/coroutines/co-await-syntax-01-outside-fn.C: New test. * g++.dg/coroutines/co-await-syntax-02-outside-fn.C: New test. * g++.dg/coroutines/co-await-syntax-03-auto.C: New test. * g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C: New test. * g++.dg/coroutines/co-await-syntax-05-constexpr.C: New test. * g++.dg/coroutines/co-await-syntax-06-main.C: New test. * g++.dg/coroutines/co-await-syntax-07-varargs.C: New test. * g++.dg/coroutines/co-await-syntax-08-lambda-auto.C: New test. * g++.dg/coroutines/co-return-syntax-01-outside-fn.C: New test. * g++.dg/coroutines/co-return-syntax-02-outside-fn.C: New test. * g++.dg/coroutines/co-return-syntax-03-auto.C: New test. * g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C: New test. * g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C: New test. * g++.dg/coroutines/co-return-syntax-06-main.C: New test. * g++.dg/coroutines/co-return-syntax-07-vararg.C: New test. * g++.dg/coroutines/co-return-syntax-08-bad-return.C: New test. * g++.dg/coroutines/co-return-syntax-09-lambda-auto.C: New test. * g++.dg/coroutines/co-yield-syntax-00-needs-expr.C: New test. * g++.dg/coroutines/co-yield-syntax-01-outside-fn.C: New test. * g++.dg/coroutines/co-yield-syntax-02-outside-fn.C: New test. * g++.dg/coroutines/co-yield-syntax-03-auto.C: New test. * g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C: New test. * g++.dg/coroutines/co-yield-syntax-05-constexpr.C: New test. * g++.dg/coroutines/co-yield-syntax-06-main.C: New test. * g++.dg/coroutines/co-yield-syntax-07-varargs.C: New test. * g++.dg/coroutines/co-yield-syntax-08-needs-expr.C: New test. * g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C: New test. * g++.dg/coroutines/coro-builtins.C: New test. * g++.dg/coroutines/coro-missing-gro.C: New test. * g++.dg/coroutines/coro-missing-promise-yield.C: New test. * g++.dg/coroutines/coro-missing-ret-value.C: New test. * g++.dg/coroutines/coro-missing-ret-void.C: New test. * g++.dg/coroutines/coro-missing-ueh-1.C: New test. * g++.dg/coroutines/coro-missing-ueh-2.C: New test. * g++.dg/coroutines/coro-missing-ueh-3.C: New test. * g++.dg/coroutines/coro-missing-ueh.h: New test. * g++.dg/coroutines/coro-pre-proc.C: New test. * g++.dg/coroutines/coro.h: New file. * g++.dg/coroutines/coro1-ret-int-yield-int.h: New file. * g++.dg/coroutines/coroutines.exp: New file. * g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C: New test. * g++.dg/coroutines/torture/alloc-01-overload-newdel.C: New test. * g++.dg/coroutines/torture/call-00-co-aw-arg.C: New test. * g++.dg/coroutines/torture/call-01-multiple-co-aw.C: New test. * g++.dg/coroutines/torture/call-02-temp-co-aw.C: New test. * g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C: New test. * g++.dg/coroutines/torture/class-00-co-ret.C: New test. * g++.dg/coroutines/torture/class-01-co-ret-parm.C: New test. * g++.dg/coroutines/torture/class-02-templ-parm.C: New test. * g++.dg/coroutines/torture/class-03-operator-templ-parm.C: New test. * g++.dg/coroutines/torture/class-04-lambda-1.C: New test. * g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C: New test. * g++.dg/coroutines/torture/class-06-lambda-capture-ref.C: New test. * g++.dg/coroutines/torture/co-await-00-trivial.C: New test. * g++.dg/coroutines/torture/co-await-01-with-value.C: New test. * g++.dg/coroutines/torture/co-await-02-xform.C: New test. * g++.dg/coroutines/torture/co-await-03-rhs-op.C: New test. * g++.dg/coroutines/torture/co-await-04-control-flow.C: New test. * g++.dg/coroutines/torture/co-await-05-loop.C: New test. * g++.dg/coroutines/torture/co-await-06-ovl.C: New test. * g++.dg/coroutines/torture/co-await-07-tmpl.C: New test. * g++.dg/coroutines/torture/co-await-08-cascade.C: New test. * g++.dg/coroutines/torture/co-await-09-pair.C: New test. * g++.dg/coroutines/torture/co-await-10-template-fn-arg.C: New test. * g++.dg/coroutines/torture/co-await-11-forwarding.C: New test. * g++.dg/coroutines/torture/co-await-12-operator-2.C: New test. * g++.dg/coroutines/torture/co-await-13-return-ref.C: New test. * g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C: New test. * g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C: New test. * g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C: New test. * g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C: New test. * g++.dg/coroutines/torture/co-ret-05-return-value.C: New test. * g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C: New test. * g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C: New test. * g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C: New test. * g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C: New test. * g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C: New test. * g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C: New test. * g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C: New test. * g++.dg/coroutines/torture/co-ret-13-template-2.C: New test. * g++.dg/coroutines/torture/co-ret-14-template-3.C: New test. * g++.dg/coroutines/torture/co-yield-00-triv.C: New test. * g++.dg/coroutines/torture/co-yield-01-multi.C: New test. * g++.dg/coroutines/torture/co-yield-02-loop.C: New test. * g++.dg/coroutines/torture/co-yield-03-tmpl.C: New test. * g++.dg/coroutines/torture/co-yield-04-complex-local-state.C: New test. * g++.dg/coroutines/torture/co-yield-05-co-aw.C: New test. * g++.dg/coroutines/torture/co-yield-06-fun-parm.C: New test. * g++.dg/coroutines/torture/co-yield-07-template-fn-param.C: New test. * g++.dg/coroutines/torture/co-yield-08-more-refs.C: New test. * g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C: New test. * g++.dg/coroutines/torture/coro-torture.exp: New file. * g++.dg/coroutines/torture/exceptions-test-0.C: New test. * g++.dg/coroutines/torture/func-params-00.C: New test. * g++.dg/coroutines/torture/func-params-01.C: New test. * g++.dg/coroutines/torture/func-params-02.C: New test. * g++.dg/coroutines/torture/func-params-03.C: New test. * g++.dg/coroutines/torture/func-params-04.C: New test. * g++.dg/coroutines/torture/func-params-05.C: New test. * g++.dg/coroutines/torture/func-params-06.C: New test. * g++.dg/coroutines/torture/lambda-00-co-ret.C: New test. * g++.dg/coroutines/torture/lambda-01-co-ret-parm.C: New test. * g++.dg/coroutines/torture/lambda-02-co-yield-values.C: New test. * g++.dg/coroutines/torture/lambda-03-auto-parm-1.C: New test. * g++.dg/coroutines/torture/lambda-04-templ-parm.C: New test. * g++.dg/coroutines/torture/lambda-05-capture-copy-local.C: New test. * g++.dg/coroutines/torture/lambda-06-multi-capture.C: New test. * g++.dg/coroutines/torture/lambda-07-multi-yield.C: New test. * g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C: New test. * g++.dg/coroutines/torture/local-var-0.C: New test. * g++.dg/coroutines/torture/local-var-1.C: New test. * g++.dg/coroutines/torture/local-var-2.C: New test. * g++.dg/coroutines/torture/local-var-3.C: New test. * g++.dg/coroutines/torture/local-var-4.C: New test. * g++.dg/coroutines/torture/mid-suspend-destruction-0.C: New test. * g++.dg/coroutines/torture/pr92933.C: New test.
This commit is contained in:
parent
472ef1d34b
commit
49789fd083
@ -1,3 +1,29 @@
|
||||
2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
|
||||
|
||||
* Makefile.in: Add coroutine-passes.o.
|
||||
* builtin-types.def (BT_CONST_SIZE): New.
|
||||
(BT_FN_BOOL_PTR): New.
|
||||
(BT_FN_PTR_PTR_CONST_SIZE_BOOL): New.
|
||||
* builtins.def (DEF_COROUTINE_BUILTIN): New.
|
||||
* coroutine-builtins.def: New file.
|
||||
* coroutine-passes.cc: New file.
|
||||
* function.h (struct GTY function): Add a bit to indicate that the
|
||||
function is a coroutine component.
|
||||
* internal-fn.c (expand_CO_FRAME): New.
|
||||
(expand_CO_YIELD): New.
|
||||
(expand_CO_SUSPN): New.
|
||||
(expand_CO_ACTOR): New.
|
||||
* internal-fn.def (CO_ACTOR): New.
|
||||
(CO_YIELD): New.
|
||||
(CO_SUSPN): New.
|
||||
(CO_FRAME): New.
|
||||
* passes.def: Add pass_coroutine_lower_builtins,
|
||||
pass_coroutine_early_expand_ifns.
|
||||
* tree-pass.h (make_pass_coroutine_lower_builtins): New.
|
||||
(make_pass_coroutine_early_expand_ifns): New.
|
||||
* doc/invoke.texi: Document the fcoroutines command line
|
||||
switch.
|
||||
|
||||
2020-01-18 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
* config/arm/vfp.md (*clear_vfp_multiple): Remove unused variable.
|
||||
|
@ -1292,6 +1292,7 @@ OBJS = \
|
||||
compare-elim.o \
|
||||
context.o \
|
||||
convert.o \
|
||||
coroutine-passes.o \
|
||||
coverage.o \
|
||||
cppbuiltin.o \
|
||||
cppdefault.o \
|
||||
|
@ -131,6 +131,8 @@ DEF_PRIMITIVE_TYPE (BT_CONST_DOUBLE_PTR,
|
||||
DEF_PRIMITIVE_TYPE (BT_LONGDOUBLE_PTR, long_double_ptr_type_node)
|
||||
DEF_PRIMITIVE_TYPE (BT_PID, pid_type_node)
|
||||
DEF_PRIMITIVE_TYPE (BT_SIZE, size_type_node)
|
||||
DEF_PRIMITIVE_TYPE (BT_CONST_SIZE,
|
||||
build_qualified_type (size_type_node, TYPE_QUAL_CONST))
|
||||
DEF_PRIMITIVE_TYPE (BT_SSIZE, signed_size_type_node)
|
||||
DEF_PRIMITIVE_TYPE (BT_WINT, wint_type_node)
|
||||
DEF_PRIMITIVE_TYPE (BT_STRING, string_type_node)
|
||||
@ -300,6 +302,7 @@ DEF_FUNCTION_TYPE_1 (BT_FN_UINT32_UINT32, BT_UINT32, BT_UINT32)
|
||||
DEF_FUNCTION_TYPE_1 (BT_FN_UINT64_UINT64, BT_UINT64, BT_UINT64)
|
||||
DEF_FUNCTION_TYPE_1 (BT_FN_UINT64_FLOAT, BT_UINT64, BT_FLOAT)
|
||||
DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_INT, BT_BOOL, BT_INT)
|
||||
DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_PTR, BT_BOOL, BT_PTR)
|
||||
DEF_FUNCTION_TYPE_1 (BT_FN_PTR_CONST_PTR, BT_PTR, BT_CONST_PTR)
|
||||
DEF_FUNCTION_TYPE_1 (BT_FN_CONST_PTR_CONST_PTR, BT_CONST_PTR, BT_CONST_PTR)
|
||||
DEF_FUNCTION_TYPE_1 (BT_FN_UINT16_UINT32, BT_UINT16, BT_UINT32)
|
||||
@ -628,6 +631,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
|
||||
BT_PTR)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
|
||||
DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
|
||||
BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
|
||||
|
||||
DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
|
||||
BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
|
||||
|
@ -189,6 +189,12 @@ along with GCC; see the file COPYING3. If not see
|
||||
DEF_BUILTIN (ENUM, NAME, BUILT_IN_NORMAL, BT_LAST, BT_LAST, false, false, \
|
||||
false, ATTR_LAST, false, false)
|
||||
|
||||
/* Builtins used in implementing coroutine support. */
|
||||
#undef DEF_COROUTINE_BUILTIN
|
||||
#define DEF_COROUTINE_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
|
||||
DEF_BUILTIN (ENUM, "__builtin_coro_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \
|
||||
true, true, true, ATTRS, true, flag_coroutines)
|
||||
|
||||
/* Builtin used by the implementation of OpenACC and OpenMP. Few of these are
|
||||
actually implemented in the compiler; most are in libgomp. */
|
||||
/* These builtins also need to be enabled in offloading compilers invoked from
|
||||
@ -1064,6 +1070,9 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
|
||||
/* Sanitizer builtins. */
|
||||
#include "sanitizer.def"
|
||||
|
||||
/* Coroutine builtins. */
|
||||
#include "coroutine-builtins.def"
|
||||
|
||||
/* Do not expose the BRIG builtins by default gcc-wide, but only privately in
|
||||
the BRIG FE as long as there are no references for them in the middle end
|
||||
or any of the upstream backends. */
|
||||
|
@ -1,3 +1,13 @@
|
||||
2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
|
||||
|
||||
* c-common.c (co_await, co_yield, co_return): New.
|
||||
* c-common.h (RID_CO_AWAIT, RID_CO_YIELD,
|
||||
RID_CO_RETURN): New enumeration values.
|
||||
(D_CXX_COROUTINES): Bit to identify coroutines are active.
|
||||
(D_CXX_COROUTINES_FLAGS): Guard for coroutine keywords.
|
||||
* c-cppbuiltin.c (__cpp_coroutines): New cpp define.
|
||||
* c.opt (fcoroutines): New command-line switch.
|
||||
|
||||
2020-01-10 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* c-format.c (local_event_ptr_node): New.
|
||||
|
@ -537,6 +537,11 @@ const struct c_common_resword c_common_reswords[] =
|
||||
{ "concept", RID_CONCEPT, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
|
||||
{ "requires", RID_REQUIRES, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
|
||||
|
||||
/* Coroutines-related keywords */
|
||||
{ "co_await", RID_CO_AWAIT, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
|
||||
{ "co_yield", RID_CO_YIELD, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
|
||||
{ "co_return", RID_CO_RETURN, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
|
||||
|
||||
/* These Objective-C keywords are recognized only immediately after
|
||||
an '@'. */
|
||||
{ "compatibility_alias", RID_AT_ALIAS, D_OBJC },
|
||||
|
@ -189,6 +189,9 @@ enum rid
|
||||
/* C++ concepts */
|
||||
RID_CONCEPT, RID_REQUIRES,
|
||||
|
||||
/* C++ coroutines */
|
||||
RID_CO_AWAIT, RID_CO_YIELD, RID_CO_RETURN,
|
||||
|
||||
/* C++ transactional memory. */
|
||||
RID_ATOMIC_NOEXCEPT, RID_ATOMIC_CANCEL, RID_SYNCHRONIZED,
|
||||
|
||||
@ -433,9 +436,11 @@ extern machine_mode c_default_pointer_mode;
|
||||
#define D_TRANSMEM 0X0800 /* C++ transactional memory TS. */
|
||||
#define D_CXX_CHAR8_T 0X1000 /* In C++, only with -fchar8_t. */
|
||||
#define D_CXX20 0x2000 /* In C++, C++20 only. */
|
||||
#define D_CXX_COROUTINES 0x4000 /* In C++, only with coroutines. */
|
||||
|
||||
#define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
|
||||
#define D_CXX_CHAR8_T_FLAGS D_CXXONLY | D_CXX_CHAR8_T
|
||||
#define D_CXX_COROUTINES_FLAGS (D_CXXONLY | D_CXX_COROUTINES)
|
||||
|
||||
/* The reserved keyword table. */
|
||||
extern const struct c_common_resword c_common_reswords[];
|
||||
|
@ -1017,6 +1017,8 @@ c_cpp_builtins (cpp_reader *pfile)
|
||||
else
|
||||
cpp_define (pfile, "__cpp_concepts=201507L");
|
||||
}
|
||||
if (flag_coroutines)
|
||||
cpp_define (pfile, "__cpp_coroutines=201902L"); /* n4835, C++20 CD */
|
||||
if (flag_tm)
|
||||
/* Use a value smaller than the 201505 specified in
|
||||
the TS, since we don't yet support atomic_cancel. */
|
||||
|
@ -1477,6 +1477,10 @@ fconstexpr-ops-limit=
|
||||
C++ ObjC++ Joined RejectNegative Host_Wide_Int Var(constexpr_ops_limit) Init(33554432)
|
||||
-fconstexpr-ops-limit=<number> Specify maximum number of constexpr operations during a single constexpr evaluation.
|
||||
|
||||
fcoroutines
|
||||
C++ LTO Var(flag_coroutines)
|
||||
Enable C++ coroutines (experimental).
|
||||
|
||||
fdebug-cpp
|
||||
C ObjC C++ ObjC++
|
||||
Emit debug annotations during preprocessing.
|
||||
|
53
gcc/coroutine-builtins.def
Normal file
53
gcc/coroutine-builtins.def
Normal file
@ -0,0 +1,53 @@
|
||||
/* This file contains the definitions and documentation for the
|
||||
coroutines builtins used in GCC.
|
||||
|
||||
Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
||||
|
||||
Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Before including this file, you should define a macro:
|
||||
|
||||
DEF_BUILTIN_STUB(ENUM, NAME)
|
||||
DEF_COROUTINE_BUILTIN (ENUM, NAME, TYPE, ATTRS)
|
||||
|
||||
See builtins.def for details.
|
||||
The builtins are created used by library implementations of C++
|
||||
coroutines. */
|
||||
|
||||
/* This has to come before all the coroutine builtins. */
|
||||
DEF_BUILTIN_STUB (BEGIN_COROUTINE_BUILTINS, (const char *) 0)
|
||||
|
||||
/* These are the builtins that are externally-visible and used by the
|
||||
standard library implementation of the coroutine header. */
|
||||
|
||||
DEF_COROUTINE_BUILTIN (BUILT_IN_CORO_PROMISE, "promise",
|
||||
BT_FN_PTR_PTR_CONST_SIZE_BOOL,
|
||||
ATTR_CONST_NOTHROW_LEAF_LIST)
|
||||
|
||||
DEF_COROUTINE_BUILTIN (BUILT_IN_CORO_RESUME, "resume", BT_FN_VOID_PTR,
|
||||
ATTR_NULL)
|
||||
|
||||
DEF_COROUTINE_BUILTIN (BUILT_IN_CORO_DESTROY, "destroy", BT_FN_VOID_PTR,
|
||||
ATTR_NULL)
|
||||
|
||||
DEF_COROUTINE_BUILTIN (BUILT_IN_CORO_DONE, "done", BT_FN_BOOL_PTR,
|
||||
ATTR_NOTHROW_LEAF_LIST)
|
||||
|
||||
/* This has to come after all the coroutine builtins. */
|
||||
DEF_BUILTIN_STUB (END_COROUTINE_BUILTINS, (const char *) 0)
|
532
gcc/coroutine-passes.cc
Normal file
532
gcc/coroutine-passes.cc
Normal file
@ -0,0 +1,532 @@
|
||||
/* coroutine expansion and optimisation passes.
|
||||
|
||||
Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
||||
|
||||
Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "backend.h"
|
||||
#include "target.h"
|
||||
#include "tree.h"
|
||||
#include "gimple.h"
|
||||
#include "tree-pass.h"
|
||||
#include "ssa.h"
|
||||
#include "cgraph.h"
|
||||
#include "pretty-print.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "fold-const.h"
|
||||
#include "internal-fn.h"
|
||||
#include "langhooks.h"
|
||||
#include "gimplify.h"
|
||||
#include "gimple-iterator.h"
|
||||
#include "gimplify-me.h"
|
||||
#include "gimple-walk.h"
|
||||
#include "gimple-fold.h"
|
||||
#include "tree-cfg.h"
|
||||
#include "tree-into-ssa.h"
|
||||
#include "tree-ssa-propagate.h"
|
||||
#include "gimple-pretty-print.h"
|
||||
#include "cfghooks.h"
|
||||
|
||||
/* Here we:
|
||||
* lower the internal function that implements an exit from scope.
|
||||
* expand the builtins that are used to implement the library
|
||||
interfaces to the coroutine frame. */
|
||||
|
||||
static tree
|
||||
lower_coro_builtin (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
||||
struct walk_stmt_info *wi ATTRIBUTE_UNUSED)
|
||||
{
|
||||
gimple *stmt = gsi_stmt (*gsi);
|
||||
*handled_ops_p = !gimple_has_substatements (stmt);
|
||||
|
||||
if (gimple_code (stmt) != GIMPLE_CALL)
|
||||
return NULL_TREE;
|
||||
|
||||
/* This internal function implements an exit from scope without
|
||||
performing any cleanups; it jumps directly to the label provided. */
|
||||
if (gimple_call_internal_p (stmt)
|
||||
&& gimple_call_internal_fn (stmt) == IFN_CO_SUSPN)
|
||||
{
|
||||
tree dest = TREE_OPERAND (gimple_call_arg (stmt, 0), 0);
|
||||
ggoto *g = gimple_build_goto (dest);
|
||||
gsi_replace (gsi, g, /* do EH */ false);
|
||||
*handled_ops_p = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
tree decl = gimple_call_fndecl (stmt);
|
||||
if (!decl || !fndecl_built_in_p (decl, BUILT_IN_NORMAL))
|
||||
return NULL_TREE;
|
||||
|
||||
/* The remaining builtins implement the library interfaces to the coro
|
||||
frame. */
|
||||
unsigned call_idx = 0;
|
||||
|
||||
switch (DECL_FUNCTION_CODE (decl))
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case BUILT_IN_CORO_PROMISE:
|
||||
{
|
||||
/* If we are discarding this, then skip it; the function has no
|
||||
side-effects. */
|
||||
tree lhs = gimple_call_lhs (stmt);
|
||||
if (!lhs)
|
||||
{
|
||||
gsi_remove (gsi, true);
|
||||
*handled_ops_p = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
/* The coro frame starts with two pointers (to the resume and
|
||||
destroy() functions). These are followed by the promise which
|
||||
is aligned as per type [or user attribute].
|
||||
The input pointer is the first argument.
|
||||
The promise alignment is the second and the third is a bool
|
||||
that is true when we are converting from a promise ptr to a
|
||||
frame pointer, and false for the inverse. */
|
||||
tree ptr = gimple_call_arg (stmt, 0);
|
||||
tree align_t = gimple_call_arg (stmt, 1);
|
||||
tree from = gimple_call_arg (stmt, 2);
|
||||
gcc_checking_assert (TREE_CODE (align_t) == INTEGER_CST);
|
||||
gcc_checking_assert (TREE_CODE (from) == INTEGER_CST);
|
||||
bool dir = wi::to_wide (from) != 0;
|
||||
HOST_WIDE_INT promise_align = TREE_INT_CST_LOW (align_t);
|
||||
HOST_WIDE_INT psize =
|
||||
TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node));
|
||||
HOST_WIDE_INT align = TYPE_ALIGN_UNIT (ptr_type_node);
|
||||
align = MAX (align, promise_align);
|
||||
psize *= 2; /* Start with two pointers. */
|
||||
psize = ROUND_UP (psize, align);
|
||||
HOST_WIDE_INT offs = dir ? -psize : psize;
|
||||
tree repl = build2 (POINTER_PLUS_EXPR, ptr_type_node, ptr,
|
||||
size_int (offs));
|
||||
gassign *grpl = gimple_build_assign (lhs, repl);
|
||||
gsi_replace (gsi, grpl, true);
|
||||
*handled_ops_p = true;
|
||||
}
|
||||
break;
|
||||
case BUILT_IN_CORO_DESTROY:
|
||||
call_idx = 1;
|
||||
/* FALLTHROUGH */
|
||||
case BUILT_IN_CORO_RESUME:
|
||||
{
|
||||
tree ptr = gimple_call_arg (stmt, 0); /* frame ptr. */
|
||||
HOST_WIDE_INT psize =
|
||||
TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node));
|
||||
HOST_WIDE_INT offset = call_idx * psize;
|
||||
tree fntype = TREE_TYPE (decl);
|
||||
tree fntype_ptr = build_pointer_type (fntype);
|
||||
tree fntype_ppp = build_pointer_type (fntype_ptr);
|
||||
tree indirect = fold_build2 (MEM_REF, fntype_ptr, ptr,
|
||||
build_int_cst (fntype_ppp, offset));
|
||||
tree f_ptr_tmp = make_ssa_name (TYPE_MAIN_VARIANT (fntype_ptr));
|
||||
gassign *get_fptr = gimple_build_assign (f_ptr_tmp, indirect);
|
||||
gsi_insert_before (gsi, get_fptr, GSI_SAME_STMT);
|
||||
gimple_call_set_fn (static_cast<gcall *> (stmt), f_ptr_tmp);
|
||||
*handled_ops_p = true;
|
||||
}
|
||||
break;
|
||||
case BUILT_IN_CORO_DONE:
|
||||
{
|
||||
/* If we are discarding this, then skip it; the function has no
|
||||
side-effects. */
|
||||
tree lhs = gimple_call_lhs (stmt);
|
||||
if (!lhs)
|
||||
{
|
||||
gsi_remove (gsi, true);
|
||||
*handled_ops_p = true;
|
||||
return NULL_TREE;
|
||||
}
|
||||
/* When we're done, the resume fn is set to NULL. */
|
||||
tree ptr = gimple_call_arg (stmt, 0); /* frame ptr. */
|
||||
tree vpp = build_pointer_type (ptr_type_node);
|
||||
tree indirect
|
||||
= fold_build2 (MEM_REF, vpp, ptr, build_int_cst (vpp, 0));
|
||||
tree d_ptr_tmp = make_ssa_name (ptr_type_node);
|
||||
gassign *get_dptr = gimple_build_assign (d_ptr_tmp, indirect);
|
||||
gsi_insert_before (gsi, get_dptr, GSI_SAME_STMT);
|
||||
tree done = fold_build2 (EQ_EXPR, boolean_type_node, d_ptr_tmp,
|
||||
null_pointer_node);
|
||||
gassign *get_res = gimple_build_assign (lhs, done);
|
||||
gsi_replace (gsi, get_res, true);
|
||||
*handled_ops_p = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Main entry point for lowering coroutine FE builtins. */
|
||||
|
||||
static unsigned int
|
||||
execute_lower_coro_builtins (void)
|
||||
{
|
||||
struct walk_stmt_info wi;
|
||||
gimple_seq body;
|
||||
|
||||
body = gimple_body (current_function_decl);
|
||||
memset (&wi, 0, sizeof (wi));
|
||||
walk_gimple_seq_mod (&body, lower_coro_builtin, NULL, &wi);
|
||||
gimple_set_body (current_function_decl, body);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const pass_data pass_data_coroutine_lower_builtins = {
|
||||
GIMPLE_PASS, /* type */
|
||||
"coro-lower-builtins", /* name */
|
||||
OPTGROUP_NONE, /* optinfo_flags */
|
||||
TV_NONE, /* tv_id */
|
||||
0, /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0 /* todo_flags_finish */
|
||||
};
|
||||
|
||||
class pass_coroutine_lower_builtins : public gimple_opt_pass
|
||||
{
|
||||
public:
|
||||
pass_coroutine_lower_builtins (gcc::context *ctxt)
|
||||
: gimple_opt_pass (pass_data_coroutine_lower_builtins, ctxt)
|
||||
{}
|
||||
|
||||
/* opt_pass methods: */
|
||||
virtual bool gate (function *) { return flag_coroutines; };
|
||||
|
||||
virtual unsigned int execute (function *f ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return execute_lower_coro_builtins ();
|
||||
}
|
||||
|
||||
}; // class pass_coroutine_lower_builtins
|
||||
|
||||
} // namespace
|
||||
|
||||
gimple_opt_pass *
|
||||
make_pass_coroutine_lower_builtins (gcc::context *ctxt)
|
||||
{
|
||||
return new pass_coroutine_lower_builtins (ctxt);
|
||||
}
|
||||
|
||||
/* Expand the remaining coroutine IFNs.
|
||||
|
||||
In the front end we construct a single actor function that contains
|
||||
the coroutine state machine.
|
||||
|
||||
The actor function has three entry conditions:
|
||||
1. from the ramp, resume point 0 - to initial-suspend.
|
||||
2. when resume () is executed (resume point N).
|
||||
3. from the destroy () shim when that is executed.
|
||||
|
||||
The actor function begins with two dispatchers; one for resume and
|
||||
one for destroy (where the initial entry from the ramp is a special-
|
||||
case of resume point 0).
|
||||
|
||||
Each suspend point and each dispatch entry is marked with an IFN such
|
||||
that we can connect the relevant dispatchers to their target labels.
|
||||
|
||||
So, if we have:
|
||||
|
||||
CO_YIELD (NUM, FINAL, RES_LAB, DEST_LAB, FRAME_PTR)
|
||||
|
||||
This is await point NUM, and is the final await if FINAL is non-zero.
|
||||
The resume point is RES_LAB, and the destroy point is DEST_LAB.
|
||||
|
||||
We expect to find a CO_ACTOR (NUM) in the resume dispatcher and a
|
||||
CO_ACTOR (NUM+1) in the destroy dispatcher.
|
||||
|
||||
Initially, the intent of keeping the resume and destroy paths together
|
||||
is that the conditionals controlling them are identical, and thus there
|
||||
would be duplication of any optimisation of those paths if the split
|
||||
were earlier.
|
||||
|
||||
Subsequent inlining of the actor (and DCE) is then able to extract the
|
||||
resume and destroy paths as separate functions if that is found
|
||||
profitable by the optimisers.
|
||||
|
||||
Once we have remade the connections to their correct postions, we elide
|
||||
the labels that the front end inserted. */
|
||||
|
||||
static void
|
||||
move_edge_and_update (edge e, basic_block old_bb, basic_block new_bb)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "redirecting edge from bb %u to bb %u\n", old_bb->index,
|
||||
new_bb->index);
|
||||
|
||||
e = redirect_edge_and_branch (e, new_bb);
|
||||
if (!e && dump_file)
|
||||
fprintf (dump_file, "failed to redirect edge .. \n");
|
||||
|
||||
/* Die if we failed. */
|
||||
gcc_checking_assert (e);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
execute_early_expand_coro_ifns (void)
|
||||
{
|
||||
/* Don't rebuild stuff unless we have to. */
|
||||
unsigned int todoflags = 0;
|
||||
bool changed = false;
|
||||
/* Some of the possible YIELD points will hopefully have been removed by
|
||||
earlier optimisations; record the ones that are still present. */
|
||||
hash_map<int_hash<HOST_WIDE_INT, -1, -2>, tree> destinations;
|
||||
/* Labels we added to carry the CFG changes, we need to remove these to
|
||||
avoid confusing EH. */
|
||||
hash_set<tree> to_remove;
|
||||
/* List of dispatch points to update. */
|
||||
auto_vec<gimple_stmt_iterator, 16> actor_worklist;
|
||||
basic_block bb;
|
||||
gimple_stmt_iterator gsi;
|
||||
|
||||
FOR_EACH_BB_FN (bb, cfun)
|
||||
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
|
||||
{
|
||||
gimple *stmt = gsi_stmt (gsi);
|
||||
|
||||
if (!is_gimple_call (stmt) || !gimple_call_internal_p (stmt))
|
||||
{
|
||||
gsi_next (&gsi);
|
||||
continue;
|
||||
}
|
||||
switch (gimple_call_internal_fn (stmt))
|
||||
{
|
||||
case IFN_CO_FRAME:
|
||||
{
|
||||
/* This internal function is a placeholder for the frame
|
||||
size. In principle, we might lower it later (after some
|
||||
optimisation had reduced the frame size). At present,
|
||||
without any such optimisation, we just set it here. */
|
||||
tree lhs = gimple_call_lhs (stmt);
|
||||
tree size = gimple_call_arg (stmt, 0);
|
||||
/* Right now, this is a trivial operation - copy through
|
||||
the size computed during initial layout. */
|
||||
gassign *grpl = gimple_build_assign (lhs, size);
|
||||
gsi_replace (&gsi, grpl, true);
|
||||
gsi_next (&gsi);
|
||||
}
|
||||
break;
|
||||
case IFN_CO_ACTOR:
|
||||
changed = true;
|
||||
actor_worklist.safe_push (gsi); /* Save for later. */
|
||||
gsi_next (&gsi);
|
||||
break;
|
||||
case IFN_CO_YIELD:
|
||||
{
|
||||
changed = true;
|
||||
/* .CO_YIELD (NUM, FINAL, RES_LAB, DEST_LAB, FRAME_PTR);
|
||||
NUM = await number.
|
||||
FINAL = 1 if this is the final_suspend() await.
|
||||
RES_LAB = resume point label.
|
||||
DEST_LAB = destroy point label.
|
||||
FRAME_PTR = is a null pointer with the type of the coro
|
||||
frame, so that we can resize, if needed. */
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "saw CO_YIELD in BB %u\n", bb->index);
|
||||
tree num = gimple_call_arg (stmt, 0); /* yield point. */
|
||||
HOST_WIDE_INT idx = TREE_INT_CST_LOW (num);
|
||||
bool existed;
|
||||
tree res_tgt = TREE_OPERAND (gimple_call_arg (stmt, 2), 0);
|
||||
tree &res_dest = destinations.get_or_insert (idx, &existed);
|
||||
if (existed && dump_file)
|
||||
{
|
||||
fprintf (
|
||||
dump_file,
|
||||
"duplicate YIELD RESUME point (" HOST_WIDE_INT_PRINT_DEC
|
||||
") ?\n",
|
||||
idx);
|
||||
print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
|
||||
}
|
||||
else
|
||||
res_dest = res_tgt;
|
||||
tree dst_tgt = TREE_OPERAND (gimple_call_arg (stmt, 3), 0);
|
||||
tree &dst_dest = destinations.get_or_insert (idx + 1, &existed);
|
||||
if (existed && dump_file)
|
||||
{
|
||||
fprintf (
|
||||
dump_file,
|
||||
"duplicate YIELD DESTROY point (" HOST_WIDE_INT_PRINT_DEC
|
||||
") ?\n",
|
||||
idx + 1);
|
||||
print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
|
||||
}
|
||||
else
|
||||
dst_dest = dst_tgt;
|
||||
to_remove.add (res_tgt);
|
||||
to_remove.add (dst_tgt);
|
||||
/* lose the co_yield. */
|
||||
gsi_remove (&gsi, true);
|
||||
stmt = gsi_stmt (gsi); /* next. */
|
||||
/* lose the copy present at O0. */
|
||||
if (is_gimple_assign (stmt))
|
||||
{
|
||||
gsi_remove (&gsi, true);
|
||||
stmt = gsi_stmt (gsi);
|
||||
}
|
||||
/* Simplify the switch or if following. */
|
||||
if (gswitch *gsw = dyn_cast<gswitch *> (stmt))
|
||||
{
|
||||
gimple_switch_set_index (gsw, integer_zero_node);
|
||||
fold_stmt (&gsi);
|
||||
}
|
||||
else if (gcond *gif = dyn_cast<gcond *> (stmt))
|
||||
{
|
||||
if (gimple_cond_code (gif) == EQ_EXPR)
|
||||
gimple_cond_make_true (gif);
|
||||
else
|
||||
gimple_cond_make_false (gif);
|
||||
fold_stmt (&gsi);
|
||||
}
|
||||
else if (dump_file)
|
||||
print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
|
||||
if (gsi_end_p (gsi))
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
gsi_next (&gsi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "coro: nothing to do\n");
|
||||
return todoflags;
|
||||
}
|
||||
|
||||
while (!actor_worklist.is_empty ())
|
||||
{
|
||||
gsi = actor_worklist.pop ();
|
||||
gimple *stmt = gsi_stmt (gsi);
|
||||
gcc_checking_assert (is_gimple_call (stmt)
|
||||
&& gimple_call_internal_p (stmt)
|
||||
&& gimple_call_internal_fn (stmt) == IFN_CO_ACTOR);
|
||||
bb = gsi_bb (gsi);
|
||||
HOST_WIDE_INT idx = TREE_INT_CST_LOW (gimple_call_arg (stmt, 0));
|
||||
tree *seen = destinations.get (idx);
|
||||
changed = true;
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "saw CO_ACTOR in BB %u\n", bb->index);
|
||||
|
||||
if (!seen)
|
||||
{
|
||||
/* If we never saw this index, it means that the CO_YIELD
|
||||
associated was elided during earlier optimisations, so we
|
||||
don't need to fix up the switch targets. */
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "yield point " HOST_WIDE_INT_PRINT_DEC
|
||||
" not used, removing it .. \n", idx);
|
||||
gsi_remove (&gsi, true);
|
||||
release_defs (stmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* So we need to switch the target of this switch case to the
|
||||
relevant BB. */
|
||||
basic_block new_bb = label_to_block (cfun, *seen);
|
||||
/* We expect the block we're modifying to contain a single
|
||||
CO_ACTOR() followed by a goto <switch default bb>. */
|
||||
gcc_checking_assert (EDGE_COUNT (bb->succs) == 1);
|
||||
edge e;
|
||||
edge_iterator ei;
|
||||
FOR_EACH_EDGE (e, ei, bb->succs)
|
||||
{
|
||||
basic_block old_bb = e->dest;
|
||||
move_edge_and_update (e, old_bb, new_bb);
|
||||
}
|
||||
gsi_remove (&gsi, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the labels we inserted to map our hidden CFG, this
|
||||
avoids confusing block merges when there are also EH labels. */
|
||||
FOR_EACH_BB_FN (bb, cfun)
|
||||
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
|
||||
{
|
||||
gimple *stmt = gsi_stmt (gsi);
|
||||
if (glabel *glab = dyn_cast<glabel *> (stmt))
|
||||
{
|
||||
tree rem = gimple_label_label (glab);
|
||||
if (to_remove.contains (rem))
|
||||
{
|
||||
gsi_remove (&gsi, true);
|
||||
to_remove.remove (rem);
|
||||
continue; /* We already moved to the next insn. */
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
gsi_next (&gsi);
|
||||
}
|
||||
|
||||
/* Changed the CFG. */
|
||||
todoflags |= TODO_cleanup_cfg;
|
||||
return todoflags;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const pass_data pass_data_coroutine_early_expand_ifns = {
|
||||
GIMPLE_PASS, /* type */
|
||||
"coro-early-expand-ifns", /* name */
|
||||
OPTGROUP_NONE, /* optinfo_flags */
|
||||
TV_NONE, /* tv_id */
|
||||
(PROP_cfg), /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0 /* todo_flags_finish, set this in the fn. */
|
||||
};
|
||||
|
||||
class pass_coroutine_early_expand_ifns : public gimple_opt_pass
|
||||
{
|
||||
public:
|
||||
pass_coroutine_early_expand_ifns (gcc::context *ctxt)
|
||||
: gimple_opt_pass (pass_data_coroutine_early_expand_ifns, ctxt)
|
||||
{}
|
||||
|
||||
/* opt_pass methods: */
|
||||
virtual bool gate (function *f)
|
||||
{
|
||||
return flag_coroutines && f->coroutine_component;
|
||||
}
|
||||
|
||||
virtual unsigned int execute (function *f ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return execute_early_expand_coro_ifns ();
|
||||
}
|
||||
|
||||
}; // class pass_coroutine_expand_ifns
|
||||
|
||||
} // namespace
|
||||
|
||||
gimple_opt_pass *
|
||||
make_pass_coroutine_early_expand_ifns (gcc::context *ctxt)
|
||||
{
|
||||
return new pass_coroutine_early_expand_ifns (ctxt);
|
||||
}
|
@ -1,3 +1,39 @@
|
||||
2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
|
||||
|
||||
* Make-lang.in: Add coroutines.o.
|
||||
* cp-tree.h (lang_decl-fn): coroutine_p, new bit.
|
||||
(DECL_COROUTINE_P): New.
|
||||
* lex.c (init_reswords): Enable keywords when the coroutine flag
|
||||
is set,
|
||||
* operators.def (co_await): New operator.
|
||||
* call.c (add_builtin_candidates): Handle CO_AWAIT_EXPR.
|
||||
(op_error): Likewise.
|
||||
(build_new_op_1): Likewise.
|
||||
(build_new_function_call): Validate coroutine builtin arguments.
|
||||
* constexpr.c (potential_constant_expression_1): Handle
|
||||
CO_AWAIT_EXPR, CO_YIELD_EXPR, CO_RETURN_EXPR.
|
||||
* coroutines.cc: New file.
|
||||
* cp-objcp-common.c (cp_common_init_ts): Add CO_AWAIT_EXPR,
|
||||
CO_YIELD_EXPR, CO_RETRN_EXPR as TS expressions.
|
||||
* cp-tree.def (CO_AWAIT_EXPR, CO_YIELD_EXPR, (CO_RETURN_EXPR): New.
|
||||
* cp-tree.h (coro_validate_builtin_call): New.
|
||||
* decl.c (emit_coro_helper): New.
|
||||
(finish_function): Handle the case when a function is found to
|
||||
be a coroutine, perform the outlining and emit the outlined
|
||||
functions. Set a bit to signal that this is a coroutine component.
|
||||
* parser.c (enum required_token): New enumeration RT_CO_YIELD.
|
||||
(cp_parser_unary_expression): Handle co_await.
|
||||
(cp_parser_assignment_expression): Handle co_yield.
|
||||
(cp_parser_statement): Handle RID_CO_RETURN.
|
||||
(cp_parser_jump_statement): Handle co_return.
|
||||
(cp_parser_operator): Handle co_await operator.
|
||||
(cp_parser_yield_expression): New.
|
||||
(cp_parser_required_error): Handle RT_CO_YIELD.
|
||||
* pt.c (tsubst_copy): Handle CO_AWAIT_EXPR.
|
||||
(tsubst_expr): Handle CO_AWAIT_EXPR, CO_YIELD_EXPR and
|
||||
CO_RETURN_EXPRs.
|
||||
* tree.c (cp_walk_subtrees): Likewise.
|
||||
|
||||
2020-01-17 Jason Merrill <jason@redhat.com>
|
||||
|
||||
PR c++/92531 - ICE with noexcept(lambda).
|
||||
|
@ -73,7 +73,7 @@ CXX_C_OBJS = attribs.o incpath.o \
|
||||
# Language-specific object files for C++ and Objective C++.
|
||||
CXX_AND_OBJCXX_OBJS = \
|
||||
cp/call.o cp/class.o cp/constexpr.o cp/constraint.o \
|
||||
cp/cp-gimplify.o \
|
||||
cp/coroutines.o cp/cp-gimplify.o \
|
||||
cp/cp-objcp-common.o cp/cp-ubsan.o \
|
||||
cp/cvt.o cp/cxx-pretty-print.o \
|
||||
cp/decl.o cp/decl2.o cp/dump.o \
|
||||
|
@ -3173,6 +3173,7 @@ add_builtin_candidates (struct z_candidate **candidates, enum tree_code code,
|
||||
case ADDR_EXPR:
|
||||
case COMPOUND_EXPR:
|
||||
case COMPONENT_REF:
|
||||
case CO_AWAIT_EXPR:
|
||||
return;
|
||||
|
||||
case COND_EXPR:
|
||||
@ -4584,6 +4585,13 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args,
|
||||
result = build_over_call (cand, flags, complain);
|
||||
}
|
||||
|
||||
if (flag_coroutines
|
||||
&& result
|
||||
&& TREE_CODE (result) == CALL_EXPR
|
||||
&& DECL_BUILT_IN_CLASS (TREE_OPERAND (CALL_EXPR_FN (result), 0))
|
||||
== BUILT_IN_NORMAL)
|
||||
result = coro_validate_builtin_call (result);
|
||||
|
||||
/* Free all the conversions we allocated. */
|
||||
obstack_free (&conversion_obstack, p);
|
||||
|
||||
@ -4942,6 +4950,16 @@ op_error (const op_location_t &loc,
|
||||
opname, opname, arg1, TREE_TYPE (arg1));
|
||||
break;
|
||||
|
||||
case CO_AWAIT_EXPR:
|
||||
if (flag_diagnostics_show_caret)
|
||||
error_at (loc, op_error_string (G_("%<operator %s%>"), 1, match),
|
||||
opname, TREE_TYPE (arg1));
|
||||
else
|
||||
error_at (loc, op_error_string (G_("%<operator %s%> in %<%s%E%>"),
|
||||
1, match),
|
||||
opname, opname, arg1, TREE_TYPE (arg1));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (arg2)
|
||||
if (flag_diagnostics_show_caret)
|
||||
@ -6197,6 +6215,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
case ADDR_EXPR:
|
||||
case COMPOUND_EXPR:
|
||||
case COMPONENT_REF:
|
||||
case CO_AWAIT_EXPR:
|
||||
result = NULL_TREE;
|
||||
result_valid_p = true;
|
||||
break;
|
||||
@ -6489,6 +6508,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
|
||||
case REALPART_EXPR:
|
||||
case IMAGPART_EXPR:
|
||||
case ABS_EXPR:
|
||||
case CO_AWAIT_EXPR:
|
||||
return cp_build_unary_op (code, arg1, false, complain);
|
||||
|
||||
case ARRAY_REF:
|
||||
|
@ -39,7 +39,7 @@ gtfiles="\
|
||||
\$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-format.c \
|
||||
\$(srcdir)/c-family/c-cppbuiltin.c \$(srcdir)/c-family/c-pragma.c \
|
||||
\$(srcdir)/cp/call.c \$(srcdir)/cp/class.c \$(srcdir)/cp/constexpr.c \
|
||||
\$(srcdir)/cp/constraint.cc \
|
||||
\$(srcdir)/cp/constraint.cc \$(srcdir)/cp/coroutines.cc \
|
||||
\$(srcdir)/cp/cp-gimplify.c \
|
||||
\$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/cp-objcp-common.c \
|
||||
\$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \
|
||||
|
@ -7852,6 +7852,12 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
|
||||
case ANNOTATE_EXPR:
|
||||
return RECUR (TREE_OPERAND (t, 0), rval);
|
||||
|
||||
/* Coroutine await, yield and return expressions are not. */
|
||||
case CO_AWAIT_EXPR:
|
||||
case CO_YIELD_EXPR:
|
||||
case CO_RETURN_EXPR:
|
||||
return false;
|
||||
|
||||
default:
|
||||
if (objc_is_property_ref (t))
|
||||
return false;
|
||||
|
3643
gcc/cp/coroutines.cc
Normal file
3643
gcc/cp/coroutines.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -551,6 +551,10 @@ cp_common_init_ts (void)
|
||||
MARK_TS_EXP (SIMPLE_REQ);
|
||||
MARK_TS_EXP (TYPE_REQ);
|
||||
|
||||
MARK_TS_EXP (CO_AWAIT_EXPR);
|
||||
MARK_TS_EXP (CO_YIELD_EXPR);
|
||||
MARK_TS_EXP (CO_RETURN_EXPR);
|
||||
|
||||
c_common_init_ts ();
|
||||
}
|
||||
|
||||
|
@ -574,6 +574,30 @@ DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
|
||||
CHECK_CONSTR_ARGUMENTS are the template arguments */
|
||||
DEFTREECODE (CHECK_CONSTR, "check_constr", tcc_expression, 2)
|
||||
|
||||
/* The co_await expression is used to support coroutines.
|
||||
|
||||
Op 0 is the cast expresssion (potentially modified by the
|
||||
promise "await_transform()" method).
|
||||
Op1 is a proxy for the temp / coro frame slot 'e' value.
|
||||
Op2 is the initialiser for Op1 (Op0, potentially modified by any
|
||||
applicable 'co_await' operator).
|
||||
Op3 is a vector of the [0] e.ready, [1] e.suspend and [2] e.resume calls.
|
||||
Op4 is a mode : 0 (await) 1 (yield) 2 (initial) 3 (final) */
|
||||
DEFTREECODE (CO_AWAIT_EXPR, "co_await", tcc_expression, 5)
|
||||
|
||||
/* The co_yield expression is used to support coroutines.
|
||||
|
||||
Op0 is the original expr (for use in diagnostics)
|
||||
Op2 is the co_await derived from this. */
|
||||
DEFTREECODE (CO_YIELD_EXPR, "co_yield", tcc_expression, 2)
|
||||
|
||||
/* The co_return expression is used to support coroutines.
|
||||
|
||||
Op0 is the original expr, can be void (for use in diagnostics)
|
||||
Op2 is the promise return_xxxx call for Op0. */
|
||||
|
||||
DEFTREECODE (CO_RETURN_EXPR, "co_return", tcc_expression, 2)
|
||||
|
||||
/*
|
||||
Local variables:
|
||||
mode:c
|
||||
|
@ -2703,7 +2703,9 @@ struct GTY(()) lang_decl_fn {
|
||||
unsigned has_dependent_explicit_spec_p : 1;
|
||||
unsigned immediate_fn_p : 1;
|
||||
unsigned maybe_deleted : 1;
|
||||
unsigned spare : 10;
|
||||
unsigned coroutine_p : 1;
|
||||
|
||||
unsigned spare : 9;
|
||||
|
||||
/* 32-bits padding on 64-bit host. */
|
||||
|
||||
@ -4994,6 +4996,13 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
|
||||
#define QUALIFIED_NAME_IS_TEMPLATE(NODE) \
|
||||
(TREE_LANG_FLAG_1 (SCOPE_REF_CHECK (NODE)))
|
||||
|
||||
/* [coroutines]
|
||||
*/
|
||||
|
||||
/* True if NODE is a co-routine FUNCTION_DECL. */
|
||||
#define DECL_COROUTINE_P(NODE) \
|
||||
(LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->coroutine_p)
|
||||
|
||||
/* True for an OMP_ATOMIC that has dependent parameters. These are stored
|
||||
as an expr in operand 1, and integer_zero_node or clauses in operand 0. */
|
||||
#define OMP_ATOMIC_DEPENDENT_P(NODE) \
|
||||
@ -7934,6 +7943,14 @@ extern tree cp_ubsan_maybe_instrument_downcast (location_t, tree, tree, tree);
|
||||
extern tree cp_ubsan_maybe_instrument_cast_to_vbase (location_t, tree, tree);
|
||||
extern void cp_ubsan_maybe_initialize_vtbl_ptrs (tree);
|
||||
|
||||
/* In coroutines.cc */
|
||||
extern tree finish_co_return_stmt (location_t, tree);
|
||||
extern tree finish_co_await_expr (location_t, tree);
|
||||
extern tree finish_co_yield_expr (location_t, tree);
|
||||
extern tree coro_validate_builtin_call (tree,
|
||||
tsubst_flags_t = tf_warning_or_error);
|
||||
extern bool morph_fn_to_coro (tree, tree *, tree *);
|
||||
|
||||
/* Inline bodies. */
|
||||
|
||||
inline tree
|
||||
|
@ -16783,6 +16783,36 @@ add_return_star_this_fixit (gcc_rich_location *richloc, tree fndecl)
|
||||
indent);
|
||||
}
|
||||
|
||||
/* This function carries out the subset of finish_function operations needed
|
||||
to emit the compiler-generated outlined helper functions used by the
|
||||
coroutines implementation. */
|
||||
|
||||
static void
|
||||
emit_coro_helper (tree helper)
|
||||
{
|
||||
/* This is a partial set of the operations done by finish_function()
|
||||
plus emitting the result. */
|
||||
set_cfun (NULL);
|
||||
current_function_decl = helper;
|
||||
begin_scope (sk_function_parms, NULL);
|
||||
store_parm_decls (DECL_ARGUMENTS (helper));
|
||||
announce_function (helper);
|
||||
allocate_struct_function (helper, false);
|
||||
cfun->language = ggc_cleared_alloc<language_function> ();
|
||||
poplevel (1, 0, 1);
|
||||
maybe_save_function_definition (helper);
|
||||
/* We must start each function with a clear fold cache. */
|
||||
clear_fold_cache ();
|
||||
cp_fold_function (helper);
|
||||
DECL_CONTEXT (DECL_RESULT (helper)) = helper;
|
||||
BLOCK_SUPERCONTEXT (DECL_INITIAL (helper)) = helper;
|
||||
/* This function has coroutine IFNs that we should handle in middle
|
||||
end lowering. */
|
||||
cfun->coroutine_component = true;
|
||||
cp_genericize (helper);
|
||||
expand_or_defer_fn (helper);
|
||||
}
|
||||
|
||||
/* Finish up a function declaration and compile that function
|
||||
all the way to assembler language output. The free the storage
|
||||
for the function definition. INLINE_P is TRUE if we just
|
||||
@ -16795,6 +16825,10 @@ finish_function (bool inline_p)
|
||||
{
|
||||
tree fndecl = current_function_decl;
|
||||
tree fntype, ctype = NULL_TREE;
|
||||
tree resumer = NULL_TREE, destroyer = NULL_TREE;
|
||||
bool coro_p = flag_coroutines
|
||||
&& !processing_template_decl
|
||||
&& DECL_COROUTINE_P (fndecl);
|
||||
|
||||
/* When we get some parse errors, we can end up without a
|
||||
current_function_decl, so cope. */
|
||||
@ -16821,6 +16855,25 @@ finish_function (bool inline_p)
|
||||
error_mark_node. */
|
||||
gcc_assert (DECL_INITIAL (fndecl) == error_mark_node);
|
||||
|
||||
if (coro_p)
|
||||
{
|
||||
if (!morph_fn_to_coro (fndecl, &resumer, &destroyer))
|
||||
{
|
||||
DECL_SAVED_TREE (fndecl) = pop_stmt_list (DECL_SAVED_TREE (fndecl));
|
||||
poplevel (1, 0, 1);
|
||||
DECL_SAVED_TREE (fndecl) = error_mark_node;
|
||||
return fndecl;
|
||||
}
|
||||
|
||||
/* We should handle coroutine IFNs in middle end lowering. */
|
||||
cfun->coroutine_component = true;
|
||||
|
||||
if (use_eh_spec_block (fndecl))
|
||||
finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS
|
||||
(TREE_TYPE (fndecl)),
|
||||
current_eh_spec_block);
|
||||
}
|
||||
else
|
||||
/* For a cloned function, we've already got all the code we need;
|
||||
there's no need to add any extra bits. */
|
||||
if (!DECL_CLONED_FUNCTION_P (fndecl))
|
||||
@ -17064,6 +17117,13 @@ finish_function (bool inline_p)
|
||||
&& !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
|
||||
cp_genericize (fndecl);
|
||||
|
||||
/* Emit the resumer and destroyer functions now. */
|
||||
if (coro_p)
|
||||
{
|
||||
emit_coro_helper (resumer);
|
||||
emit_coro_helper (destroyer);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
/* We're leaving the context of this function, so zap cfun. It's still in
|
||||
DECL_STRUCT_FUNCTION, and we'll restore it in tree_rest_of_compilation. */
|
||||
|
@ -233,6 +233,8 @@ init_reswords (void)
|
||||
mask |= D_CXX20;
|
||||
if (!flag_concepts)
|
||||
mask |= D_CXX_CONCEPTS;
|
||||
if (!flag_coroutines)
|
||||
mask |= D_CXX_COROUTINES;
|
||||
if (!flag_tm)
|
||||
mask |= D_TRANSMEM;
|
||||
if (!flag_char8_t)
|
||||
|
@ -87,6 +87,7 @@ DEF_OPERATOR ("++", PREINCREMENT_EXPR, "pp", OVL_OP_FLAG_UNARY)
|
||||
DEF_OPERATOR ("--", PREDECREMENT_EXPR, "mm", OVL_OP_FLAG_UNARY)
|
||||
DEF_OPERATOR ("->", COMPONENT_REF, "pt", OVL_OP_FLAG_UNARY)
|
||||
DEF_OPERATOR ("sizeof", SIZEOF_EXPR, "sz", OVL_OP_FLAG_UNARY)
|
||||
DEF_OPERATOR ("co_await", CO_AWAIT_EXPR, "aw", OVL_OP_FLAG_UNARY)
|
||||
|
||||
/* These are extensions. */
|
||||
DEF_OPERATOR ("alignof", ALIGNOF_EXPR, "az", OVL_OP_FLAG_UNARY)
|
||||
|
@ -177,7 +177,9 @@ enum required_token {
|
||||
RT_CLASS_TYPENAME_TEMPLATE, /* class, typename, or template */
|
||||
RT_TRANSACTION_ATOMIC, /* __transaction_atomic */
|
||||
RT_TRANSACTION_RELAXED, /* __transaction_relaxed */
|
||||
RT_TRANSACTION_CANCEL /* __transaction_cancel */
|
||||
RT_TRANSACTION_CANCEL, /* __transaction_cancel */
|
||||
|
||||
RT_CO_YIELD /* co_yield */
|
||||
};
|
||||
|
||||
/* RAII wrapper for parser->in_type_id_in_expr_p, setting it on creation and
|
||||
@ -2471,6 +2473,12 @@ static void cp_parser_function_transaction
|
||||
static tree cp_parser_transaction_cancel
|
||||
(cp_parser *);
|
||||
|
||||
/* Coroutine extensions. */
|
||||
|
||||
static tree cp_parser_yield_expression
|
||||
(cp_parser *);
|
||||
|
||||
|
||||
enum pragma_context {
|
||||
pragma_external,
|
||||
pragma_member,
|
||||
@ -8112,6 +8120,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
|
||||
postfix-expression
|
||||
++ cast-expression
|
||||
-- cast-expression
|
||||
await-expression
|
||||
unary-operator cast-expression
|
||||
sizeof unary-expression
|
||||
sizeof ( type-id )
|
||||
@ -8326,6 +8335,22 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
|
||||
noexcept_loc);
|
||||
}
|
||||
|
||||
case RID_CO_AWAIT:
|
||||
{
|
||||
tree expr;
|
||||
location_t kw_loc = token->location;
|
||||
|
||||
/* Consume the `co_await' token. */
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
/* Parse its cast-expression. */
|
||||
expr = cp_parser_simple_cast_expression (parser);
|
||||
if (expr == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
/* Handle [expr.await]. */
|
||||
return cp_expr (finish_co_await_expr (kw_loc, expr));
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -9757,6 +9782,7 @@ cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
|
||||
conditional-expression
|
||||
logical-or-expression assignment-operator assignment_expression
|
||||
throw-expression
|
||||
yield-expression
|
||||
|
||||
CAST_P is true if this expression is the target of a cast.
|
||||
DECLTYPE_P is true if this expression is the operand of decltype.
|
||||
@ -9773,6 +9799,10 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
|
||||
a throw-expression. */
|
||||
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_THROW))
|
||||
expr = cp_parser_throw_expression (parser);
|
||||
/* If the next token is the `co_yield' keyword, then we're looking at
|
||||
a yield-expression. */
|
||||
else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CO_YIELD))
|
||||
expr = cp_parser_yield_expression (parser);
|
||||
/* Otherwise, it must be that we are looking at a
|
||||
logical-or-expression. */
|
||||
else
|
||||
@ -11271,6 +11301,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
|
||||
case RID_BREAK:
|
||||
case RID_CONTINUE:
|
||||
case RID_RETURN:
|
||||
case RID_CO_RETURN:
|
||||
case RID_GOTO:
|
||||
std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
|
||||
statement = cp_parser_jump_statement (parser);
|
||||
@ -12915,6 +12946,7 @@ cp_parser_init_statement (cp_parser *parser, tree *decl)
|
||||
continue ;
|
||||
return expression [opt] ;
|
||||
return braced-init-list ;
|
||||
coroutine-return-statement;
|
||||
goto identifier ;
|
||||
|
||||
GNU extension:
|
||||
@ -12985,6 +13017,7 @@ cp_parser_jump_statement (cp_parser* parser)
|
||||
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
|
||||
break;
|
||||
|
||||
case RID_CO_RETURN:
|
||||
case RID_RETURN:
|
||||
{
|
||||
tree expr;
|
||||
@ -13002,8 +13035,11 @@ cp_parser_jump_statement (cp_parser* parser)
|
||||
/* If the next token is a `;', then there is no
|
||||
expression. */
|
||||
expr = NULL_TREE;
|
||||
/* Build the return-statement. */
|
||||
if (FNDECL_USED_AUTO (current_function_decl) && in_discarded_stmt)
|
||||
/* Build the return-statement, check co-return first, since type
|
||||
deduction is not valid there. */
|
||||
if (keyword == RID_CO_RETURN)
|
||||
statement = finish_co_return_stmt (token->location, expr);
|
||||
else if (FNDECL_USED_AUTO (current_function_decl) && in_discarded_stmt)
|
||||
/* Don't deduce from a discarded return statement. */;
|
||||
else
|
||||
statement = finish_return_stmt (expr);
|
||||
@ -15383,22 +15419,25 @@ cp_parser_operator (cp_parser* parser, location_t start_loc)
|
||||
{
|
||||
case CPP_KEYWORD:
|
||||
{
|
||||
/* The keyword should be either `new' or `delete'. */
|
||||
/* The keyword should be either `new', `delete' or `co_await'. */
|
||||
if (token->keyword == RID_NEW)
|
||||
op = NEW_EXPR;
|
||||
else if (token->keyword == RID_DELETE)
|
||||
op = DELETE_EXPR;
|
||||
else if (token->keyword == RID_CO_AWAIT)
|
||||
op = CO_AWAIT_EXPR;
|
||||
else
|
||||
break;
|
||||
|
||||
/* Consume the `new' or `delete' token. */
|
||||
/* Consume the `new', `delete' or co_await token. */
|
||||
end_loc = cp_lexer_consume_token (parser->lexer)->location;
|
||||
|
||||
/* Peek at the next token. */
|
||||
token = cp_lexer_peek_token (parser->lexer);
|
||||
/* If it's a `[' token then this is the array variant of the
|
||||
operator. */
|
||||
if (token->type == CPP_OPEN_SQUARE)
|
||||
if (token->type == CPP_OPEN_SQUARE
|
||||
&& op != CO_AWAIT_EXPR)
|
||||
{
|
||||
/* Consume the `[' token. */
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
@ -26085,6 +26124,41 @@ cp_parser_throw_expression (cp_parser* parser)
|
||||
return expression;
|
||||
}
|
||||
|
||||
/* Parse a yield-expression.
|
||||
|
||||
yield-expression:
|
||||
co_yield assignment-expression
|
||||
co_yield braced-init-list
|
||||
|
||||
Returns a CO_YIELD_EXPR representing the yield-expression. */
|
||||
|
||||
static tree
|
||||
cp_parser_yield_expression (cp_parser* parser)
|
||||
{
|
||||
tree expr;
|
||||
|
||||
cp_token *token = cp_lexer_peek_token (parser->lexer);
|
||||
location_t kw_loc = token->location; /* Save for later. */
|
||||
|
||||
cp_parser_require_keyword (parser, RID_CO_YIELD, RT_CO_YIELD);
|
||||
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
|
||||
{
|
||||
bool expr_non_constant_p;
|
||||
cp_lexer_set_source_position (parser->lexer);
|
||||
/* ??? : probably a moot point? */
|
||||
maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
|
||||
expr = cp_parser_braced_list (parser, &expr_non_constant_p);
|
||||
}
|
||||
else
|
||||
expr = cp_parser_assignment_expression (parser);
|
||||
|
||||
if (expr == error_mark_node)
|
||||
return expr;
|
||||
|
||||
return finish_co_yield_expr (kw_loc, expr);
|
||||
}
|
||||
|
||||
/* GNU Extensions */
|
||||
|
||||
/* Parse an (optional) asm-specification.
|
||||
@ -30337,6 +30411,9 @@ cp_parser_required_error (cp_parser *parser,
|
||||
case RT_TRANSACTION_RELAXED:
|
||||
gmsgid = G_("expected %<__transaction_relaxed%>");
|
||||
break;
|
||||
case RT_CO_YIELD:
|
||||
gmsgid = G_("expected %<co_yield%>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
21
gcc/cp/pt.c
21
gcc/cp/pt.c
@ -16814,6 +16814,11 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
|
||||
to the containing function, inlined copy or so. */
|
||||
return t;
|
||||
|
||||
case CO_AWAIT_EXPR:
|
||||
return tsubst_expr (t, args, complain, in_decl,
|
||||
/*integral_constant_expression_p=*/false);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* We shouldn't get here, but keep going if !flag_checking. */
|
||||
if (flag_checking)
|
||||
@ -17680,6 +17685,22 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
|
||||
finish_return_stmt (RECUR (TREE_OPERAND (t, 0)));
|
||||
break;
|
||||
|
||||
case CO_RETURN_EXPR:
|
||||
finish_co_return_stmt (input_location, RECUR (TREE_OPERAND (t, 0)));
|
||||
break;
|
||||
|
||||
case CO_YIELD_EXPR:
|
||||
stmt = finish_co_yield_expr (input_location,
|
||||
RECUR (TREE_OPERAND (t, 0)));
|
||||
RETURN (stmt);
|
||||
break;
|
||||
|
||||
case CO_AWAIT_EXPR:
|
||||
stmt = finish_co_await_expr (input_location,
|
||||
RECUR (TREE_OPERAND (t, 0)));
|
||||
RETURN (stmt);
|
||||
break;
|
||||
|
||||
case EXPR_STMT:
|
||||
tmp = RECUR (EXPR_STMT_EXPR (t));
|
||||
if (EXPR_STMT_STMT_EXPR_RESULT (t))
|
||||
|
@ -5068,6 +5068,37 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
|
||||
WALK_SUBTREE (TREE_VALUE (cap));
|
||||
break;
|
||||
|
||||
case CO_YIELD_EXPR:
|
||||
if (TREE_OPERAND (*tp, 1))
|
||||
/* Operand 1 is the tree for the relevant co_await which has any
|
||||
interesting sub-trees. */
|
||||
WALK_SUBTREE (TREE_OPERAND (*tp, 1));
|
||||
break;
|
||||
|
||||
case CO_AWAIT_EXPR:
|
||||
if (TREE_OPERAND (*tp, 1))
|
||||
/* Operand 1 is frame variable. */
|
||||
WALK_SUBTREE (TREE_OPERAND (*tp, 1));
|
||||
if (TREE_OPERAND (*tp, 2))
|
||||
/* Operand 2 has the initialiser, and we need to walk any subtrees
|
||||
there. */
|
||||
WALK_SUBTREE (TREE_OPERAND (*tp, 2));
|
||||
break;
|
||||
|
||||
case CO_RETURN_EXPR:
|
||||
if (TREE_OPERAND (*tp, 0))
|
||||
{
|
||||
if (VOID_TYPE_P (TREE_OPERAND (*tp, 0)))
|
||||
/* For void expressions, operand 1 is a trivial call, and any
|
||||
interesting subtrees will be part of operand 0. */
|
||||
WALK_SUBTREE (TREE_OPERAND (*tp, 0));
|
||||
else if (TREE_OPERAND (*tp, 1))
|
||||
/* Interesting sub-trees will be in the return_value () call
|
||||
arguments. */
|
||||
WALK_SUBTREE (TREE_OPERAND (*tp, 1));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
@ -2628,6 +2628,10 @@ of a loop too many expressions need to be evaluated, the resulting constexpr
|
||||
evaluation might take too long.
|
||||
The default is 33554432 (1<<25).
|
||||
|
||||
@item -fcoroutines
|
||||
@opindex fcoroutines
|
||||
Enable support for the C++ coroutines extension (experimental).
|
||||
|
||||
@item -fno-elide-constructors
|
||||
@opindex fno-elide-constructors
|
||||
@opindex felide-constructors
|
||||
|
@ -418,6 +418,9 @@ struct GTY(()) function {
|
||||
/* Set when the function was compiled with generation of debug
|
||||
(begin stmt, inline entry, ...) markers enabled. */
|
||||
unsigned int debug_nonbind_markers : 1;
|
||||
|
||||
/* Set if this is a coroutine-related function. */
|
||||
unsigned int coroutine_component : 1;
|
||||
};
|
||||
|
||||
/* Add the decl D to the local_decls list of FUN. */
|
||||
|
@ -2884,6 +2884,32 @@ expand_NOP (internal_fn, gcall *)
|
||||
/* Nothing. But it shouldn't really prevail. */
|
||||
}
|
||||
|
||||
/* Coroutines, all should have been processed at this stage. */
|
||||
|
||||
static void
|
||||
expand_CO_FRAME (internal_fn, gcall *)
|
||||
{
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
static void
|
||||
expand_CO_YIELD (internal_fn, gcall *)
|
||||
{
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
static void
|
||||
expand_CO_SUSPN (internal_fn, gcall *)
|
||||
{
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
static void
|
||||
expand_CO_ACTOR (internal_fn, gcall *)
|
||||
{
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Expand a call to FN using the operands in STMT. FN has a single
|
||||
output operand and NARGS input operands. */
|
||||
|
||||
|
@ -366,6 +366,12 @@ DEF_INTERNAL_FN (LAUNDER, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
|
||||
/* Divmod function. */
|
||||
DEF_INTERNAL_FN (DIVMOD, ECF_CONST | ECF_LEAF, NULL)
|
||||
|
||||
/* For coroutines. */
|
||||
DEF_INTERNAL_FN (CO_ACTOR, ECF_NOTHROW | ECF_LEAF, NULL)
|
||||
DEF_INTERNAL_FN (CO_YIELD, ECF_NOTHROW, NULL)
|
||||
DEF_INTERNAL_FN (CO_SUSPN, ECF_NOTHROW, NULL)
|
||||
DEF_INTERNAL_FN (CO_FRAME, ECF_PURE | ECF_NOTHROW | ECF_LEAF, NULL)
|
||||
|
||||
/* A NOP function with arbitrary arguments and return value. */
|
||||
DEF_INTERNAL_FN (NOP, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
|
||||
|
||||
|
@ -39,8 +39,10 @@ along with GCC; see the file COPYING3. If not see
|
||||
NEXT_PASS (pass_lower_tm);
|
||||
NEXT_PASS (pass_refactor_eh);
|
||||
NEXT_PASS (pass_lower_eh);
|
||||
NEXT_PASS (pass_coroutine_lower_builtins);
|
||||
NEXT_PASS (pass_build_cfg);
|
||||
NEXT_PASS (pass_warn_function_return);
|
||||
NEXT_PASS (pass_coroutine_early_expand_ifns);
|
||||
NEXT_PASS (pass_expand_omp);
|
||||
NEXT_PASS (pass_warn_printf);
|
||||
NEXT_PASS (pass_walloca, /*strict_mode_p=*/true);
|
||||
|
@ -1,3 +1,123 @@
|
||||
2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
|
||||
|
||||
* g++.dg/coroutines/co-await-syntax-00-needs-expr.C: New test.
|
||||
* g++.dg/coroutines/co-await-syntax-01-outside-fn.C: New test.
|
||||
* g++.dg/coroutines/co-await-syntax-02-outside-fn.C: New test.
|
||||
* g++.dg/coroutines/co-await-syntax-03-auto.C: New test.
|
||||
* g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C: New test.
|
||||
* g++.dg/coroutines/co-await-syntax-05-constexpr.C: New test.
|
||||
* g++.dg/coroutines/co-await-syntax-06-main.C: New test.
|
||||
* g++.dg/coroutines/co-await-syntax-07-varargs.C: New test.
|
||||
* g++.dg/coroutines/co-await-syntax-08-lambda-auto.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-01-outside-fn.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-02-outside-fn.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-03-auto.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-06-main.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-07-vararg.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-08-bad-return.C: New test.
|
||||
* g++.dg/coroutines/co-return-syntax-09-lambda-auto.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-00-needs-expr.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-01-outside-fn.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-02-outside-fn.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-03-auto.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-05-constexpr.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-06-main.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-07-varargs.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-08-needs-expr.C: New test.
|
||||
* g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C: New test.
|
||||
* g++.dg/coroutines/coro-builtins.C: New test.
|
||||
* g++.dg/coroutines/coro-missing-gro.C: New test.
|
||||
* g++.dg/coroutines/coro-missing-promise-yield.C: New test.
|
||||
* g++.dg/coroutines/coro-missing-ret-value.C: New test.
|
||||
* g++.dg/coroutines/coro-missing-ret-void.C: New test.
|
||||
* g++.dg/coroutines/coro-missing-ueh-1.C: New test.
|
||||
* g++.dg/coroutines/coro-missing-ueh-2.C: New test.
|
||||
* g++.dg/coroutines/coro-missing-ueh-3.C: New test.
|
||||
* g++.dg/coroutines/coro-missing-ueh.h: New test.
|
||||
* g++.dg/coroutines/coro-pre-proc.C: New test.
|
||||
* g++.dg/coroutines/coro.h: New file.
|
||||
* g++.dg/coroutines/coro1-ret-int-yield-int.h: New file.
|
||||
* g++.dg/coroutines/coroutines.exp: New file.
|
||||
* g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C: New test.
|
||||
* g++.dg/coroutines/torture/alloc-01-overload-newdel.C: New test.
|
||||
* g++.dg/coroutines/torture/call-00-co-aw-arg.C: New test.
|
||||
* g++.dg/coroutines/torture/call-01-multiple-co-aw.C: New test.
|
||||
* g++.dg/coroutines/torture/call-02-temp-co-aw.C: New test.
|
||||
* g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C: New test.
|
||||
* g++.dg/coroutines/torture/class-00-co-ret.C: New test.
|
||||
* g++.dg/coroutines/torture/class-01-co-ret-parm.C: New test.
|
||||
* g++.dg/coroutines/torture/class-02-templ-parm.C: New test.
|
||||
* g++.dg/coroutines/torture/class-03-operator-templ-parm.C: New test.
|
||||
* g++.dg/coroutines/torture/class-04-lambda-1.C: New test.
|
||||
* g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C: New test.
|
||||
* g++.dg/coroutines/torture/class-06-lambda-capture-ref.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-00-trivial.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-01-with-value.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-02-xform.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-03-rhs-op.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-04-control-flow.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-05-loop.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-06-ovl.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-07-tmpl.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-08-cascade.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-09-pair.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-10-template-fn-arg.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-11-forwarding.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-12-operator-2.C: New test.
|
||||
* g++.dg/coroutines/torture/co-await-13-return-ref.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-05-return-value.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-13-template-2.C: New test.
|
||||
* g++.dg/coroutines/torture/co-ret-14-template-3.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-00-triv.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-01-multi.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-02-loop.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-03-tmpl.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-04-complex-local-state.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-05-co-aw.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-06-fun-parm.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-07-template-fn-param.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-08-more-refs.C: New test.
|
||||
* g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C: New test.
|
||||
* g++.dg/coroutines/torture/coro-torture.exp: New file.
|
||||
* g++.dg/coroutines/torture/exceptions-test-0.C: New test.
|
||||
* g++.dg/coroutines/torture/func-params-00.C: New test.
|
||||
* g++.dg/coroutines/torture/func-params-01.C: New test.
|
||||
* g++.dg/coroutines/torture/func-params-02.C: New test.
|
||||
* g++.dg/coroutines/torture/func-params-03.C: New test.
|
||||
* g++.dg/coroutines/torture/func-params-04.C: New test.
|
||||
* g++.dg/coroutines/torture/func-params-05.C: New test.
|
||||
* g++.dg/coroutines/torture/func-params-06.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-00-co-ret.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-01-co-ret-parm.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-02-co-yield-values.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-03-auto-parm-1.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-04-templ-parm.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-05-capture-copy-local.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-06-multi-capture.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-07-multi-yield.C: New test.
|
||||
* g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C: New test.
|
||||
* g++.dg/coroutines/torture/local-var-0.C: New test.
|
||||
* g++.dg/coroutines/torture/local-var-1.C: New test.
|
||||
* g++.dg/coroutines/torture/local-var-2.C: New test.
|
||||
* g++.dg/coroutines/torture/local-var-3.C: New test.
|
||||
* g++.dg/coroutines/torture/local-var-4.C: New test.
|
||||
* g++.dg/coroutines/torture/mid-suspend-destruction-0.C: New test.
|
||||
* g++.dg/coroutines/torture/pr92933.C: New test.
|
||||
|
||||
2020-01-17 Jerry DeLisle <jvdelisle@gcc.gnu.org>
|
||||
|
||||
PR libfortran/93234
|
||||
|
@ -0,0 +1,7 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
void bar () {
|
||||
co_await; // { dg-error "expected primary-expression before" }
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
int x = co_await coro::suspend_always{}; // { dg-error {'co_await' cannot be used outside a function} }
|
@ -0,0 +1,5 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
auto f (int x = co_await coro::suspend_always{}); // { dg-error {'co_await' cannot be used outside a function} }
|
16
gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
Normal file
16
gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
Normal file
@ -0,0 +1,16 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
extern struct awaitable *aw ();
|
||||
|
||||
auto bar () {
|
||||
int x = 1 + co_await *aw(); // { dg-error "cannot be used in a function with a deduced return type" }
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
int main () {
|
||||
bar ();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
struct Foo {
|
||||
Foo () { co_await coro::suspend_always{}; } // { dg-error "cannot be used in a constructor" }
|
||||
~Foo () { co_await coro::suspend_always{}; } // { dg-error "cannot be used in a destructor" }
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
constexpr int bar () {
|
||||
co_await coro::suspend_always{}; // { dg-error "cannot be used in a .constexpr. function" }
|
||||
return 42; /* Suppress the "no return" error. */
|
||||
}
|
||||
|
||||
int main () {
|
||||
return bar ();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
co_await coro::suspend_always{}; // { dg-error "cannot be used in the .main. function" }
|
||||
}
|
14
gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
Normal file
14
gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
Normal file
@ -0,0 +1,14 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
int
|
||||
bar (int x, ...)
|
||||
{
|
||||
co_await coro::suspend_always{}; // { dg-error "cannot be used in a varargs function" }
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
bar (5, ac);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
// Check that we decline return type deduction for lambda coroutines.
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "coro1-ret-int-yield-int.h"
|
||||
|
||||
int main ()
|
||||
{
|
||||
/* Attempt to deduce the return type for a lambda coroutine. */
|
||||
auto f = []()
|
||||
{
|
||||
co_await coro::suspend_always{}; // { dg-error "cannot be used in a function with a deduced return type" }
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
co_return; // { dg-error {expected unqualified-id before 'co_return'} }
|
||||
|
@ -0,0 +1,5 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
auto f (co_return); // { dg-error {expected primary-expression before 'co_return'} }
|
12
gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
Normal file
12
gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
Normal file
@ -0,0 +1,12 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
auto bar () {
|
||||
co_return 5; // { dg-error "cannot be used in a function with a deduced return type" }
|
||||
}
|
||||
|
||||
int main () {
|
||||
bar ();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
struct Foo {
|
||||
Foo () { co_return; } // { dg-error "cannot be used in a constructor" }
|
||||
~Foo () { co_return 5; } // { dg-error "cannot be used in a destructor" }
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
constexpr int bar () {
|
||||
co_return 5; // { dg-error "cannot be used in a .constexpr. function" }
|
||||
return 42; /* Suppress the "no return" error. */
|
||||
}
|
||||
|
||||
int main () {
|
||||
return bar ();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
co_return 0; // { dg-error "cannot be used in the .main. function" }
|
||||
}
|
14
gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
Normal file
14
gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
Normal file
@ -0,0 +1,14 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
int
|
||||
bar (int x, ...)
|
||||
{
|
||||
co_return 1; // { dg-error "cannot be used in a varargs function" }
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
bar (5, ac);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
struct Coro {
|
||||
struct promise_type;
|
||||
using handle_type = coro::coroutine_handle<Coro::promise_type>;
|
||||
handle_type handle;
|
||||
Coro () : handle(0) {}
|
||||
Coro (handle_type _handle) : handle(_handle) {}
|
||||
Coro (Coro &&s) : handle(s.handle) { s.handle = nullptr; }
|
||||
Coro &operator = (Coro &&s) {
|
||||
handle = s.handle;
|
||||
s.handle = nullptr;
|
||||
return *this;
|
||||
}
|
||||
Coro (const Coro &) = delete;
|
||||
~Coro() {
|
||||
if ( handle )
|
||||
handle.destroy();
|
||||
}
|
||||
struct promise_type {
|
||||
promise_type() {}
|
||||
~promise_type() {}
|
||||
Coro get_return_object () { return Coro (handle_type::from_promise (*this)); }
|
||||
auto initial_suspend () { return coro::suspend_always{}; }
|
||||
auto final_suspend () { return coro::suspend_always{}; }
|
||||
void return_void () { }
|
||||
void unhandled_exception() { }
|
||||
};
|
||||
};
|
||||
|
||||
extern int x;
|
||||
|
||||
// Diagnose disallowed "return" in coroutine.
|
||||
Coro
|
||||
bar () // { dg-error {a 'return' statement is not allowed} }
|
||||
{
|
||||
if (x)
|
||||
return Coro();
|
||||
else
|
||||
co_return;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
// Check that we decline return type deduction for lambda coroutines.
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "coro1-ret-int-yield-int.h"
|
||||
|
||||
int main ()
|
||||
{
|
||||
/* Attempt to deduce the return type for a lambda coroutine. */
|
||||
auto f = []()
|
||||
{
|
||||
co_return 42; // { dg-error "cannot be used in a function with a deduced return type" }
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
void foo () {
|
||||
co_yield; // { dg-error "expected primary-expression before" }
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
auto f (int x = co_yield 5); // { dg-error {'co_yield' cannot be used outside a function} }
|
||||
|
@ -0,0 +1,6 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
|
||||
|
12
gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
Normal file
12
gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
Normal file
@ -0,0 +1,12 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
auto bar () {
|
||||
co_yield 5; // { dg-error "cannot be used in a function with a deduced return type" }
|
||||
}
|
||||
|
||||
int main () {
|
||||
bar ();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
struct Foo {
|
||||
Foo () { co_yield 4; } // { dg-error "cannot be used in a constructor" }
|
||||
~Foo () { co_yield 4; } // { dg-error "cannot be used in a destructor" }
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
constexpr int bar () {
|
||||
co_yield 5; // { dg-error "cannot be used in a .constexpr. function" }
|
||||
return 42; /* Suppress the "no return" error. */
|
||||
}
|
||||
|
||||
int main () {
|
||||
return bar ();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
co_yield 0; // { dg-error "cannot be used in the .main. function" }
|
||||
}
|
14
gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
Normal file
14
gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
Normal file
@ -0,0 +1,14 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
int
|
||||
bar (int x, ...)
|
||||
{
|
||||
co_yield 1; // { dg-error "cannot be used in a varargs function" }
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
bar (5, ac);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
// Check syntax for missing expr in a coroutine context.
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
struct DummyYield {
|
||||
coro::coroutine_handle<> handle;
|
||||
DummyYield () : handle (nullptr) {}
|
||||
DummyYield (coro::coroutine_handle<> handle) : handle (handle) {}
|
||||
struct dummy_yield {
|
||||
coro::suspend_never initial_suspend() { return {}; }
|
||||
coro::suspend_never final_suspend() { return {}; }
|
||||
DummyYield get_return_object() {
|
||||
return DummyYield (coro::coroutine_handle<dummy_yield>::from_promise (*this));
|
||||
}
|
||||
void yield_value (int v) {}
|
||||
void return_value (int v) {}
|
||||
void unhandled_exception() { /*std::terminate();*/ };
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct coro::coroutine_traits<DummyYield> {
|
||||
using promise_type = DummyYield::dummy_yield;
|
||||
};
|
||||
|
||||
DummyYield
|
||||
bar ()
|
||||
{
|
||||
co_yield; // { dg-error {expected primary-expression before} }
|
||||
co_return 0;
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
DummyYield x = bar ();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
// Check that we decline return type deduction for lambda coroutines.
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "coro1-ret-int-yield-int.h"
|
||||
|
||||
int main ()
|
||||
{
|
||||
/* Attempt to deduce the return type for a lambda coroutine. */
|
||||
auto f = []()
|
||||
{
|
||||
co_yield 42; // { dg-error "cannot be used in a function with a deduced return type" }
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
17
gcc/testsuite/g++.dg/coroutines/coro-builtins.C
Normal file
17
gcc/testsuite/g++.dg/coroutines/coro-builtins.C
Normal file
@ -0,0 +1,17 @@
|
||||
// { dg-additional-options "-fsyntax-only " }
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
int main ()
|
||||
{
|
||||
void *co_h;
|
||||
void *promise;
|
||||
const size_t co_align = 16;
|
||||
|
||||
bool d = __builtin_coro_done (co_h);
|
||||
__builtin_coro_resume (co_h);
|
||||
__builtin_coro_destroy (co_h);
|
||||
promise = __builtin_coro_promise (co_h, co_align, true);
|
||||
|
||||
return 0;
|
||||
}
|
32
gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
Normal file
32
gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
Normal file
@ -0,0 +1,32 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
// Diagose missing get_return_object() in the promise type.
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
struct MissingGRO {
|
||||
coro::coroutine_handle<> handle;
|
||||
MissingGRO () : handle (nullptr) {}
|
||||
MissingGRO (coro::coroutine_handle<> handle) : handle (handle) {}
|
||||
struct missing_gro {
|
||||
coro::suspend_never initial_suspend() { return {}; }
|
||||
coro::suspend_never final_suspend() { return {}; }
|
||||
void return_void () {}
|
||||
void unhandled_exception() { /*std::terminate();*/ };
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct coro::coroutine_traits<MissingGRO> {
|
||||
using promise_type = MissingGRO::missing_gro;
|
||||
};
|
||||
|
||||
MissingGRO
|
||||
bar () // { dg-error {no member named 'get_return_object' in} }
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
MissingGRO x = bar ();
|
||||
return 0;
|
||||
}
|
33
gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
Normal file
33
gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
Normal file
@ -0,0 +1,33 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
#include "coro.h"
|
||||
|
||||
struct MissingPromiseYield {
|
||||
coro::coroutine_handle<> handle;
|
||||
MissingPromiseYield () : handle (nullptr) {}
|
||||
MissingPromiseYield (coro::coroutine_handle<> handle) : handle (handle) {}
|
||||
struct missing_yield {
|
||||
coro::suspend_never initial_suspend() { return {}; }
|
||||
coro::suspend_never final_suspend() { return {}; }
|
||||
MissingPromiseYield get_return_object() {
|
||||
return MissingPromiseYield (coro::coroutine_handle<missing_yield>::from_promise (*this));
|
||||
}
|
||||
void return_value (int v) {}
|
||||
void unhandled_exception() { /*std::terminate();*/ };
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct coro::coroutine_traits<MissingPromiseYield> {
|
||||
using promise_type = MissingPromiseYield::missing_yield;
|
||||
};
|
||||
|
||||
MissingPromiseYield
|
||||
bar ()
|
||||
{
|
||||
co_yield 22; // { dg-error {no member named 'yield_value' in} }
|
||||
co_return 0;
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
MissingPromiseYield x = bar ();
|
||||
return 0;
|
||||
}
|
34
gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
Normal file
34
gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
Normal file
@ -0,0 +1,34 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
// Diagose missing return_value() in the promise type.
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
struct MissingRetValue {
|
||||
coro::coroutine_handle<> handle;
|
||||
MissingRetValue () : handle (nullptr) {}
|
||||
MissingRetValue (coro::coroutine_handle<> handle) : handle (handle) {}
|
||||
struct missing_retvoid {
|
||||
coro::suspend_never initial_suspend() { return {}; }
|
||||
coro::suspend_never final_suspend() { return {}; }
|
||||
MissingRetValue get_return_object() {
|
||||
return MissingRetValue (coro::coroutine_handle<missing_retvoid>::from_promise (*this));
|
||||
}
|
||||
void unhandled_exception() { /*std::terminate();*/ };
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct coro::coroutine_traits<MissingRetValue> {
|
||||
using promise_type = MissingRetValue::missing_retvoid;
|
||||
};
|
||||
|
||||
MissingRetValue
|
||||
bar ()
|
||||
{
|
||||
co_return 6174; // { dg-error {no member named 'return_value' in} }
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
MissingRetValue x = bar ();
|
||||
return 0;
|
||||
}
|
34
gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
Normal file
34
gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
Normal file
@ -0,0 +1,34 @@
|
||||
// { dg-additional-options "-fsyntax-only -w" }
|
||||
|
||||
#include "coro.h"
|
||||
|
||||
// Diagose missing return_void() in the promise type.
|
||||
|
||||
struct MissingRetVoid {
|
||||
coro::coroutine_handle<> handle;
|
||||
MissingRetVoid () : handle (nullptr) {}
|
||||
MissingRetVoid (coro::coroutine_handle<> handle) : handle (handle) {}
|
||||
struct missing_retvoid {
|
||||
coro::suspend_never initial_suspend() { return {}; }
|
||||
coro::suspend_never final_suspend() { return {}; }
|
||||
MissingRetVoid get_return_object() {
|
||||
return MissingRetVoid (coro::coroutine_handle<missing_retvoid>::from_promise (*this));
|
||||
}
|
||||
void unhandled_exception() { /*std::terminate();*/ };
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct coro::coroutine_traits<MissingRetVoid> {
|
||||
using promise_type = MissingRetVoid::missing_retvoid;
|
||||
};
|
||||
|
||||
MissingRetVoid
|
||||
bar ()
|
||||
{
|
||||
co_return; // { dg-error "no member named .return_void. in" }
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
MissingRetVoid x = bar ();
|
||||
return 0;
|
||||
}
|
17
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
Normal file
17
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
Normal file
@ -0,0 +1,17 @@
|
||||
// { dg-additional-options "-fsyntax-only -fexceptions -w" }
|
||||
|
||||
// Diagose missing unhandled_exception() in the promise type.
|
||||
|
||||
#include "coro.h"
|
||||
#include "coro-missing-ueh.h"
|
||||
|
||||
MissingUEH
|
||||
bar () // { dg-error {no member named 'unhandled_exception' in} }
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
MissingUEH x = bar ();
|
||||
return 0;
|
||||
}
|
18
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
Normal file
18
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
Normal file
@ -0,0 +1,18 @@
|
||||
// { dg-additional-options "-fsyntax-only -fno-exceptions " }
|
||||
|
||||
// The missing method is warned for when exceptions are off and pedantic
|
||||
// is on (default in the testsuite).
|
||||
|
||||
#include "coro.h"
|
||||
#include "coro-missing-ueh.h"
|
||||
|
||||
MissingUEH
|
||||
bar () // { dg-warning {no member named 'unhandled_exception' in} }
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
MissingUEH x = bar ();
|
||||
return 0;
|
||||
}
|
18
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
Normal file
18
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
Normal file
@ -0,0 +1,18 @@
|
||||
// { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" }
|
||||
|
||||
/* We don't warn about the missing method, unless in pedantic mode, so
|
||||
this compile should be clean. */
|
||||
|
||||
#include "coro.h"
|
||||
#include "coro-missing-ueh.h"
|
||||
|
||||
MissingUEH
|
||||
bar ()
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
int main (int ac, char *av[]) {
|
||||
MissingUEH x = bar ();
|
||||
return 0;
|
||||
}
|
23
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
Normal file
23
gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef __MissingUEH_H
|
||||
#define __MissingUEH_H
|
||||
|
||||
/* Common code for testing missing unhandled_exception. */
|
||||
struct MissingUEH {
|
||||
coro::coroutine_handle<> handle;
|
||||
MissingUEH () : handle (nullptr) {}
|
||||
MissingUEH (coro::coroutine_handle<> handle) : handle (handle) {}
|
||||
struct missing_ueh {
|
||||
coro::suspend_never initial_suspend() { return {}; }
|
||||
coro::suspend_never final_suspend() { return {}; }
|
||||
MissingUEH get_return_object() {
|
||||
return MissingUEH (coro::coroutine_handle<missing_ueh>::from_promise (*this));
|
||||
}
|
||||
void return_void () {}
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct coro::coroutine_traits<MissingUEH> {
|
||||
using promise_type = MissingUEH::missing_ueh;
|
||||
};
|
||||
|
||||
#endif
|
9
gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
Normal file
9
gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
Normal file
@ -0,0 +1,9 @@
|
||||
// Only need to compile this, with the default options from the .exp.
|
||||
|
||||
#ifndef __cpp_coroutines
|
||||
#error "coroutines should engaged."
|
||||
#endif
|
||||
|
||||
#if __cpp_coroutines != 201902L
|
||||
#error "coroutine version out of sync."
|
||||
#endif
|
152
gcc/testsuite/g++.dg/coroutines/coro.h
Normal file
152
gcc/testsuite/g++.dg/coroutines/coro.h
Normal file
@ -0,0 +1,152 @@
|
||||
#if __has_include(<coroutine>)
|
||||
|
||||
#include <coroutine>
|
||||
|
||||
# if __clang__
|
||||
# include <utility>
|
||||
# endif
|
||||
|
||||
namespace coro = std;
|
||||
|
||||
#elif __has_include(<experimental/coroutine>)
|
||||
|
||||
#include <experimental/coroutine>
|
||||
|
||||
# if __clang__
|
||||
# include <utility>
|
||||
# endif
|
||||
|
||||
namespace coro = std::experimental;
|
||||
|
||||
#else
|
||||
|
||||
#warning "no installed coroutine headers found, using test-suite local one"
|
||||
|
||||
/* Dummy version to allow tests without an installed header. */
|
||||
# ifndef __TESTSUITE_CORO_H_n4835
|
||||
# define __TESTSUITE_CORO_H_n4835
|
||||
|
||||
// Fragments (with short-cuts) to mimic enough of the library header to
|
||||
// make some progress.
|
||||
|
||||
# if __cpp_coroutines
|
||||
|
||||
namespace std {
|
||||
inline namespace __n4835 {
|
||||
|
||||
// 21.11.1 coroutine traits
|
||||
template<typename _R, typename...> struct coroutine_traits {
|
||||
using promise_type = typename _R::promise_type;
|
||||
};
|
||||
|
||||
// 21.11.2 coroutine handle
|
||||
template <typename Promise = void> struct coroutine_handle;
|
||||
|
||||
template <>
|
||||
struct coroutine_handle<void> {
|
||||
public:
|
||||
// 21.11.2.1 construct/reset
|
||||
constexpr coroutine_handle () noexcept
|
||||
: __fr_ptr (0) {}
|
||||
constexpr coroutine_handle (decltype(nullptr) __h) noexcept
|
||||
: __fr_ptr (__h) {}
|
||||
coroutine_handle &operator= (decltype(nullptr)) noexcept {
|
||||
__fr_ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
// 21.11.2.2 export/import
|
||||
constexpr void *address () const noexcept { return __fr_ptr; }
|
||||
constexpr static coroutine_handle from_address (void *__a) noexcept {
|
||||
coroutine_handle __self;
|
||||
__self.__fr_ptr = __a;
|
||||
return __self;
|
||||
}
|
||||
public:
|
||||
// 21.11.2.3 observers
|
||||
constexpr explicit operator bool () const noexcept {
|
||||
return bool (__fr_ptr);
|
||||
}
|
||||
bool done () const noexcept {
|
||||
return __builtin_coro_done (__fr_ptr);
|
||||
}
|
||||
// 21.11.2.4 resumption
|
||||
void operator () () const { resume (); }
|
||||
void resume () const {
|
||||
__builtin_coro_resume (__fr_ptr);
|
||||
}
|
||||
void destroy () const {
|
||||
__builtin_coro_destroy (__fr_ptr);
|
||||
}
|
||||
protected:
|
||||
void *__fr_ptr;
|
||||
};
|
||||
|
||||
template <class _Promise>
|
||||
struct coroutine_handle : coroutine_handle<> {
|
||||
// 21.11.2.1 construct/reset
|
||||
using coroutine_handle<>::coroutine_handle;
|
||||
static coroutine_handle from_promise(_Promise &p) {
|
||||
coroutine_handle __self;
|
||||
__self.__fr_ptr =
|
||||
__builtin_coro_promise((char *)&p, __alignof(_Promise), true);
|
||||
return __self;
|
||||
}
|
||||
coroutine_handle& operator=(decltype(nullptr)) noexcept {
|
||||
coroutine_handle<>::operator=(nullptr);
|
||||
return *this;
|
||||
}
|
||||
// 21.11.2.2 export/import
|
||||
constexpr static coroutine_handle from_address(void* __a){
|
||||
coroutine_handle __self;
|
||||
__self.__fr_ptr = __a;
|
||||
return __self;
|
||||
}
|
||||
// 21.11.2.5 promise access
|
||||
_Promise& promise() const {
|
||||
void * __t = __builtin_coro_promise(this->__fr_ptr,
|
||||
__alignof(_Promise), false);
|
||||
return *static_cast<_Promise*>(__t);
|
||||
}
|
||||
};
|
||||
|
||||
// n4760 - 21.11.5 trivial awaitables
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend(coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct suspend_never {
|
||||
bool await_ready() { return true; }
|
||||
void await_suspend(coroutine_handle<>) {}
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
} // namespace __n4835
|
||||
} // namespace std
|
||||
|
||||
namespace coro = std;
|
||||
|
||||
# else
|
||||
# error "coro.h requires support for coroutines, add -fcoroutines"
|
||||
# endif
|
||||
# endif // __TESTSUITE_CORO_H_n4835
|
||||
|
||||
#endif // __has_include(<experimental/coroutine>)
|
||||
|
||||
/* just to avoid cluttering dump files. */
|
||||
extern "C" int puts (const char *);
|
||||
extern "C" int printf (const char *, ...);
|
||||
|
||||
#include <cstdlib> /* for abort () */
|
||||
|
||||
#ifndef OUTPUT
|
||||
# define PRINT(X)
|
||||
# define PRINTF (void)
|
||||
#else
|
||||
# define PRINT(X) puts(X)
|
||||
# define PRINTF printf
|
||||
#endif
|
133
gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
Normal file
133
gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
Normal file
@ -0,0 +1,133 @@
|
||||
struct coro1 {
|
||||
struct promise_type;
|
||||
using handle_type = coro::coroutine_handle<coro1::promise_type>;
|
||||
handle_type handle;
|
||||
coro1 () : handle(0) {}
|
||||
coro1 (handle_type _handle)
|
||||
: handle(_handle) {
|
||||
PRINT("Created coro1 object from handle");
|
||||
}
|
||||
coro1 (const coro1 &) = delete; // no copying
|
||||
coro1 (coro1 &&s) : handle(s.handle) {
|
||||
s.handle = nullptr;
|
||||
PRINT("coro1 mv ctor ");
|
||||
}
|
||||
coro1 &operator = (coro1 &&s) {
|
||||
handle = s.handle;
|
||||
s.handle = nullptr;
|
||||
PRINT("coro1 op= ");
|
||||
return *this;
|
||||
}
|
||||
~coro1() {
|
||||
PRINT("Destroyed coro1");
|
||||
if ( handle )
|
||||
handle.destroy();
|
||||
}
|
||||
|
||||
// Some awaitables to use in tests.
|
||||
// With progress printing for debug.
|
||||
struct suspend_never_prt {
|
||||
bool await_ready() const noexcept { return true; }
|
||||
void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
|
||||
void await_resume() const noexcept { PRINT ("susp-never-resume");}
|
||||
};
|
||||
|
||||
struct suspend_always_prt {
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
|
||||
void await_resume() const noexcept { PRINT ("susp-always-resume");}
|
||||
~suspend_always_prt() { PRINT ("susp-always-dtor"); }
|
||||
};
|
||||
|
||||
struct suspend_always_intprt {
|
||||
int x;
|
||||
suspend_always_intprt() : x(5) {}
|
||||
suspend_always_intprt(int __x) : x(__x) {}
|
||||
~suspend_always_intprt() {}
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-intprt");}
|
||||
int await_resume() const noexcept { PRINT ("susp-always-resume-intprt"); return x;}
|
||||
};
|
||||
|
||||
/* This returns the square of the int that it was constructed with. */
|
||||
struct suspend_always_longprtsq {
|
||||
long x;
|
||||
suspend_always_longprtsq() : x(12L) { PRINT ("suspend_always_longprtsq def ctor"); }
|
||||
suspend_always_longprtsq(long _x) : x(_x) { PRINTF ("suspend_always_longprtsq ctor with %ld\n", x); }
|
||||
~suspend_always_longprtsq() {}
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-longsq");}
|
||||
long await_resume() const noexcept { PRINT ("susp-always-resume-longsq"); return x * x;}
|
||||
};
|
||||
|
||||
struct suspend_always_intrefprt {
|
||||
int& x;
|
||||
suspend_always_intrefprt(int& __x) : x(__x) {}
|
||||
~suspend_always_intrefprt() {}
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-intprt");}
|
||||
int& await_resume() const noexcept { PRINT ("susp-always-resume-intprt"); return x;}
|
||||
};
|
||||
|
||||
struct promise_type {
|
||||
|
||||
promise_type() : vv(-1) { PRINT ("Created Promise"); }
|
||||
promise_type(int __x) : vv(__x) { PRINTF ("Created Promise with %d\n",__x); }
|
||||
~promise_type() { PRINT ("Destroyed Promise"); }
|
||||
|
||||
auto get_return_object () {
|
||||
PRINT ("get_return_object: handle from promise");
|
||||
return handle_type::from_promise (*this);
|
||||
}
|
||||
|
||||
auto initial_suspend () {
|
||||
PRINT ("get initial_suspend (always)");
|
||||
return suspend_always_prt{};
|
||||
}
|
||||
auto final_suspend () {
|
||||
PRINT ("get final_suspend (always)");
|
||||
return suspend_always_prt{};
|
||||
}
|
||||
|
||||
#ifdef USE_AWAIT_TRANSFORM
|
||||
|
||||
auto await_transform (int v) {
|
||||
PRINTF ("await_transform an int () %d\n",v);
|
||||
return suspend_always_intprt (v);
|
||||
}
|
||||
|
||||
auto await_transform (long v) {
|
||||
PRINTF ("await_transform a long () %ld\n",v);
|
||||
return suspend_always_longprtsq (v);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
auto yield_value (int v) {
|
||||
PRINTF ("yield_value (%d)\n", v);
|
||||
vv = v;
|
||||
return suspend_always_prt{};
|
||||
}
|
||||
|
||||
#ifdef RETURN_VOID
|
||||
|
||||
void return_void () {
|
||||
PRINT ("return_void ()");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void return_value (int v) {
|
||||
PRINTF ("return_value (%d)\n", v);
|
||||
vv = v;
|
||||
}
|
||||
|
||||
#endif
|
||||
void unhandled_exception() { PRINT ("** unhandled exception"); }
|
||||
|
||||
int get_value () { return vv; }
|
||||
private:
|
||||
int vv;
|
||||
};
|
||||
|
||||
};
|
50
gcc/testsuite/g++.dg/coroutines/coroutines.exp
Normal file
50
gcc/testsuite/g++.dg/coroutines/coroutines.exp
Normal file
@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
||||
|
||||
# Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GCC; see the file COPYING3. If not see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Test C++ coroutines, requires c++17; doesn't, at present, seem much
|
||||
# point in repeating these for other versions.
|
||||
|
||||
# Load support procs.
|
||||
load_lib g++-dg.exp
|
||||
|
||||
# If a testcase doesn't have special options, use these.
|
||||
global DEFAULT_CXXFLAGS
|
||||
if ![info exists DEFAULT_CXXFLAGS] then {
|
||||
set DEFAULT_CXXFLAGS " -pedantic-errors -Wno-long-long"
|
||||
}
|
||||
|
||||
set DEFAULT_COROFLAGS $DEFAULT_CXXFLAGS
|
||||
lappend DEFAULT_COROFLAGS "-std=c++17" "-fcoroutines"
|
||||
|
||||
dg-init
|
||||
|
||||
# Run the tests.
|
||||
# g++-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] \
|
||||
# "" $DEFAULT_COROFLAGS
|
||||
|
||||
foreach test [lsort [find $srcdir/$subdir {*.[CH]}]] {
|
||||
if [runtest_file_p $runtests $test] {
|
||||
set nshort [file tail [file dirname $test]]/[file tail $test]
|
||||
verbose "Testing $nshort $DEFAULT_COROFLAGS" 1
|
||||
dg-test $test "" $DEFAULT_COROFLAGS
|
||||
set testcase [string range $test [string length "$srcdir/"] end]
|
||||
}
|
||||
}
|
||||
|
||||
# done.
|
||||
dg-finish
|
@ -0,0 +1,118 @@
|
||||
// { dg-do run }
|
||||
|
||||
// check the code-gen for the failed alloc return.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
#if __has_include(<new>)
|
||||
# include <new>
|
||||
#else
|
||||
|
||||
// Required when get_return_object_on_allocation_failure() is defined by
|
||||
// the promise.
|
||||
// we need a no-throw new, and new etc. build the relevant pieces here to
|
||||
// avoid needing the headers in the test.
|
||||
|
||||
namespace std {
|
||||
struct nothrow_t {};
|
||||
constexpr nothrow_t nothrow = {};
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
} // end namespace std
|
||||
|
||||
void* operator new(std::size_t, const std::nothrow_t&) noexcept;
|
||||
void operator delete(void* __p, const std::nothrow_t&) noexcept;
|
||||
#endif
|
||||
|
||||
struct coro1 {
|
||||
struct promise_type;
|
||||
using handle_type = coro::coroutine_handle<coro1::promise_type>;
|
||||
handle_type handle;
|
||||
coro1 () noexcept : handle(0) {}
|
||||
coro1 (handle_type _handle) noexcept
|
||||
: handle(_handle) {
|
||||
PRINT("Created coro1 object from handle");
|
||||
}
|
||||
coro1 (const coro1 &) = delete; // no copying
|
||||
coro1 (coro1 &&s) noexcept : handle(s.handle) {
|
||||
s.handle = nullptr;
|
||||
PRINT("coro1 mv ctor ");
|
||||
}
|
||||
coro1 &operator = (coro1 &&s) noexcept {
|
||||
handle = s.handle;
|
||||
s.handle = nullptr;
|
||||
PRINT("coro1 op= ");
|
||||
return *this;
|
||||
}
|
||||
~coro1() noexcept {
|
||||
PRINT("Destroyed coro1");
|
||||
if ( handle )
|
||||
handle.destroy();
|
||||
}
|
||||
|
||||
struct suspend_never_prt {
|
||||
bool await_ready() const noexcept { return true; }
|
||||
void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
|
||||
void await_resume() const noexcept { PRINT ("susp-never-resume");}
|
||||
~suspend_never_prt() {};
|
||||
};
|
||||
|
||||
struct suspend_always_prt {
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
|
||||
void await_resume() const noexcept { PRINT ("susp-always-resume");}
|
||||
};
|
||||
|
||||
struct promise_type {
|
||||
promise_type() { PRINT ("Created Promise"); }
|
||||
~promise_type() { PRINT ("Destroyed Promise"); }
|
||||
|
||||
auto get_return_object () {
|
||||
PRINT ("get_return_object: handle from promise");
|
||||
return handle_type::from_promise (*this);
|
||||
}
|
||||
auto initial_suspend () {
|
||||
PRINT ("get initial_suspend (always)");
|
||||
return suspend_always_prt{};
|
||||
}
|
||||
auto final_suspend () {
|
||||
PRINT ("get final_suspend (always)");
|
||||
return suspend_always_prt{};
|
||||
}
|
||||
void return_void () {
|
||||
PRINT ("return_void ()");
|
||||
}
|
||||
void unhandled_exception() { PRINT ("** unhandled exception"); }
|
||||
static coro1 get_return_object_on_allocation_failure () noexcept;
|
||||
}; // promise
|
||||
}; // coro1
|
||||
|
||||
coro1 coro1::promise_type::
|
||||
get_return_object_on_allocation_failure () noexcept {
|
||||
PRINT ("alloc fail return");
|
||||
return coro1 (nullptr);
|
||||
}
|
||||
|
||||
struct coro1
|
||||
f () noexcept
|
||||
{
|
||||
PRINT ("coro1: about to return");
|
||||
co_return;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 x = f ();
|
||||
PRINT ("main: got coro1 - resuming");
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume");
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
// { dg-do run }
|
||||
|
||||
// check codegen for overloaded operator new/delete.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
int used_ovl_new = 0;
|
||||
int used_ovl_del = 0;
|
||||
|
||||
struct coro1 {
|
||||
struct promise_type;
|
||||
using handle_type = coro::coroutine_handle<coro1::promise_type>;
|
||||
handle_type handle;
|
||||
coro1 () noexcept : handle(0) {}
|
||||
coro1 (handle_type _handle) noexcept
|
||||
: handle(_handle) {
|
||||
PRINT("Created coro1 object from handle");
|
||||
}
|
||||
coro1 (const coro1 &) = delete; // no copying
|
||||
coro1 (coro1 &&s) noexcept : handle(s.handle) {
|
||||
s.handle = nullptr;
|
||||
PRINT("coro1 mv ctor ");
|
||||
}
|
||||
coro1 &operator = (coro1 &&s) noexcept {
|
||||
handle = s.handle;
|
||||
s.handle = nullptr;
|
||||
PRINT("coro1 op= ");
|
||||
return *this;
|
||||
}
|
||||
~coro1() noexcept {
|
||||
PRINT("Destroyed coro1");
|
||||
if ( handle )
|
||||
handle.destroy();
|
||||
}
|
||||
|
||||
struct suspend_never_prt {
|
||||
bool await_ready() const noexcept { return true; }
|
||||
void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
|
||||
void await_resume() const noexcept { PRINT ("susp-never-resume");}
|
||||
~suspend_never_prt() {};
|
||||
};
|
||||
|
||||
struct suspend_always_prt {
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
|
||||
void await_resume() const noexcept { PRINT ("susp-always-resume");}
|
||||
};
|
||||
|
||||
struct promise_type {
|
||||
promise_type() { PRINT ("Created Promise"); }
|
||||
~promise_type() { PRINT ("Destroyed Promise"); }
|
||||
|
||||
void *operator new (std::size_t sz) {
|
||||
PRINT ("promise_type: used overloaded operator new");
|
||||
used_ovl_new++;
|
||||
return ::operator new(sz);
|
||||
}
|
||||
|
||||
void operator delete (void *p) {
|
||||
PRINT ("promise_type: used overloaded operator delete");
|
||||
used_ovl_del++;
|
||||
return ::operator delete(p);
|
||||
}
|
||||
|
||||
auto get_return_object () {
|
||||
PRINT ("get_return_object: handle from promise");
|
||||
return handle_type::from_promise (*this);
|
||||
}
|
||||
auto initial_suspend () {
|
||||
PRINT ("get initial_suspend (always)");
|
||||
return suspend_always_prt{};
|
||||
}
|
||||
auto final_suspend () {
|
||||
PRINT ("get final_suspend (always)");
|
||||
return suspend_always_prt{};
|
||||
}
|
||||
void return_void () {
|
||||
PRINT ("return_void ()");
|
||||
}
|
||||
void unhandled_exception() { PRINT ("** unhandled exception"); }
|
||||
}; // promise
|
||||
}; // coro1
|
||||
|
||||
struct coro1
|
||||
f () noexcept
|
||||
{
|
||||
PRINT ("coro1: about to return");
|
||||
co_return;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
// Nest a scope so that we can inspect the flags after the DTORs run.
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 x = f ();
|
||||
PRINT ("main: got coro1 - resuming");
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume");
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
if (used_ovl_new != 1)
|
||||
{
|
||||
PRINT ("main: failed to call overloaded operator new");
|
||||
abort ();
|
||||
}
|
||||
if (used_ovl_del != 1)
|
||||
{
|
||||
PRINT ("main: failed to call overloaded operator delete");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
73
gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
Normal file
73
gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
Normal file
@ -0,0 +1,73 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check that we can use co_await as a call parm.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
__attribute__((__noinline__))
|
||||
static int
|
||||
foo (int x)
|
||||
{
|
||||
return x + 2;
|
||||
}
|
||||
|
||||
/* Function with a single await. */
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
gX = foo (co_await 9);
|
||||
co_return gX + 31;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
|
||||
PRINT ("main: resuming [1] (initial suspend)");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [2] (await 9 parm)");
|
||||
f_coro.handle.resume();
|
||||
|
||||
if (gX != 11)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* we should now have returned with the co_return 11 + 31) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done'");
|
||||
abort ();
|
||||
}
|
||||
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 42)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 42\n", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check that we can use multiple co_awaits as a call parm.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
__attribute__((__noinline__))
|
||||
static int
|
||||
bar (int x, int y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
|
||||
/* Function with a multiple awaits. */
|
||||
coro1
|
||||
g ()
|
||||
{
|
||||
gX = bar (co_await 9, co_await 2);
|
||||
co_return gX + 31;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 g_coro = g ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (g_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
|
||||
PRINT ("main: resuming [1] (initial suspend)");
|
||||
g_coro.handle.resume();
|
||||
|
||||
PRINT ("main: resuming [2] (parm 1)");
|
||||
g_coro.handle.resume();
|
||||
PRINT ("main: resuming [2] (parm 2)");
|
||||
g_coro.handle.resume();
|
||||
if (gX != 11)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* we should now have returned with the co_return 11 + 31) */
|
||||
if (!g_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done'");
|
||||
abort ();
|
||||
}
|
||||
|
||||
int y = g_coro.handle.promise().get_value();
|
||||
if (y != 42)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 42\n", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
72
gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
Normal file
72
gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
Normal file
@ -0,0 +1,72 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check foo (compiler temp, co_await).
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
__attribute__((__noinline__))
|
||||
static int
|
||||
bar (int x, int y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
|
||||
/* Function with a compiler temporary and a co_await. */
|
||||
coro1
|
||||
g ()
|
||||
{
|
||||
gX = bar (gX + 8, co_await 2);
|
||||
co_return gX + 31;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 g_coro = g ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (g_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
|
||||
PRINT ("main: resuming [1] (initial suspend)");
|
||||
g_coro.handle.resume();
|
||||
|
||||
PRINT ("main: resuming [2] (parm 1)");
|
||||
g_coro.handle.resume();
|
||||
|
||||
if (gX != 11)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* we should now have returned with the co_return 11 + 31) */
|
||||
if (!g_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done'");
|
||||
abort ();
|
||||
}
|
||||
|
||||
int y = g_coro.handle.promise().get_value();
|
||||
if (y != 42)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 42\n", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check foo (compiler temp, co_await).
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
__attribute__((__noinline__))
|
||||
static int
|
||||
bar (int x, const int& y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
|
||||
/* Function with a compiler temporary and a co_await. */
|
||||
coro1
|
||||
g ()
|
||||
{
|
||||
gX = bar (gX + 8, co_await 2);
|
||||
co_return gX + 31;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 g_coro = g ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (g_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
|
||||
PRINT ("main: resuming [1] (initial suspend)");
|
||||
g_coro.handle.resume();
|
||||
|
||||
PRINT ("main: resuming [2] (parm 1)");
|
||||
g_coro.handle.resume();
|
||||
|
||||
if (gX != 11)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* we should now have returned with the co_return 11 + 31) */
|
||||
if (!g_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done'");
|
||||
abort ();
|
||||
}
|
||||
|
||||
int y = g_coro.handle.promise().get_value();
|
||||
if (y != 42)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 42\n", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
41
gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
Normal file
41
gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
Normal file
@ -0,0 +1,41 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Simplest class.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
class foo
|
||||
{
|
||||
public:
|
||||
coro1 meth ()
|
||||
{
|
||||
PRINT ("coro1: about to return");
|
||||
co_return 42;
|
||||
}
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
foo inst;
|
||||
|
||||
PRINT ("main: create coro1");
|
||||
coro1 x = inst.meth ();
|
||||
PRINT ("main: got coro1 - resuming");
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume");
|
||||
int y = x.handle.promise().get_value();
|
||||
if ( y != 42 )
|
||||
abort ();
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Class with parm capture
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
class foo
|
||||
{
|
||||
public:
|
||||
coro1 meth (int x)
|
||||
{
|
||||
if (x > 30)
|
||||
{
|
||||
PRINT ("coro1: about to return k");
|
||||
co_return 6174;
|
||||
}
|
||||
else if (x > 20)
|
||||
{
|
||||
PRINT ("coro1: about to return the answer");
|
||||
co_return 42;
|
||||
}
|
||||
else
|
||||
{
|
||||
PRINT ("coro1: about to return 0");
|
||||
co_return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
foo inst;
|
||||
|
||||
PRINT ("main: create coro1");
|
||||
coro1 x = inst.meth (25);
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume");
|
||||
int y = x.handle.promise().get_value();
|
||||
if ( y != 42 )
|
||||
{
|
||||
PRINTF ("main: wrong result (%d)", y);
|
||||
abort ();
|
||||
}
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
// { dg-do run }
|
||||
|
||||
// template parm in a class
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
template <typename T>
|
||||
class foo
|
||||
{
|
||||
public:
|
||||
coro1 meth (T y)
|
||||
{
|
||||
PRINT ("coro1: about to return");
|
||||
T x = y;
|
||||
co_return co_await x + 3;
|
||||
}
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
foo<int> inst {};
|
||||
PRINT ("main: create coro1");
|
||||
coro1 x = inst.meth (17);
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (initial suspend)");
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (co_await)");
|
||||
|
||||
/* Now we should have the co_returned value. */
|
||||
int y = x.handle.promise().get_value();
|
||||
if ( y != 20 )
|
||||
{
|
||||
PRINTF ("main: wrong result (%d).", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
// { dg-do run }
|
||||
|
||||
// template parm in a class
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
template <typename T>
|
||||
class foo
|
||||
{
|
||||
public:
|
||||
coro1 operator()(T y)
|
||||
{
|
||||
PRINT ("coro1: about to return");
|
||||
T x = y;
|
||||
co_return co_await x + 3;
|
||||
}
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
foo<int> inst {};
|
||||
PRINT ("main: create coro1");
|
||||
coro1 x = inst.operator()(17);
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (initial suspend)");
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (co_await)");
|
||||
|
||||
/* Now we should have the co_returned value. */
|
||||
int y = x.handle.promise().get_value();
|
||||
if ( y != 20 )
|
||||
{
|
||||
PRINTF ("main: wrong result (%d).", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
58
gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
Normal file
58
gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
Normal file
@ -0,0 +1,58 @@
|
||||
// { dg-do run }
|
||||
|
||||
// template parm in a class
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
template <typename T>
|
||||
class foo
|
||||
{
|
||||
public:
|
||||
auto get_lam ()
|
||||
{
|
||||
auto l = [](T y) -> coro1
|
||||
{
|
||||
T x = y;
|
||||
co_return co_await x + 3;
|
||||
};
|
||||
return l;
|
||||
}
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
foo<int> inst {};
|
||||
auto ll = inst.get_lam ();
|
||||
|
||||
PRINT ("main: create coro1");
|
||||
int arg = 17; // avoid a dangling reference
|
||||
coro1 x = ll (arg);
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (initial suspend)");
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (co_await)");
|
||||
|
||||
/* Now we should have the co_returned value. */
|
||||
int y = x.handle.promise().get_value();
|
||||
if ( y != 20 )
|
||||
{
|
||||
PRINTF ("main: wrong result (%d).", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
// { dg-do run }
|
||||
|
||||
// template parm in a class
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
template <typename T>
|
||||
class foo
|
||||
{
|
||||
public:
|
||||
auto get_lam (int parm)
|
||||
{
|
||||
int local = 3;
|
||||
auto l = [=](T y) -> coro1
|
||||
{
|
||||
T x = y;
|
||||
co_return co_await x + local;
|
||||
};
|
||||
return l;
|
||||
}
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
foo<int> inst {};
|
||||
auto ll = inst.get_lam (10);
|
||||
|
||||
PRINT ("main: create coro1");
|
||||
int arg = 17; // avoid a dangling reference
|
||||
coro1 x = ll (arg);
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (initial suspend)");
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (co_await)");
|
||||
|
||||
/* Now we should have the co_returned value. */
|
||||
int y = x.handle.promise().get_value();
|
||||
if ( y != 20 )
|
||||
{
|
||||
PRINTF ("main: wrong result (%d).", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
// { dg-do run }
|
||||
|
||||
// template parm in a class
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
template <typename T>
|
||||
class foo
|
||||
{
|
||||
public:
|
||||
void use_lambda ()
|
||||
{
|
||||
int a_copy = 20;
|
||||
int a_ref = 10;
|
||||
|
||||
auto f = [&, a_copy]() -> coro1
|
||||
{
|
||||
co_yield a_ref + a_copy;
|
||||
co_return a_ref + a_copy;
|
||||
};
|
||||
|
||||
coro1 A = f ();
|
||||
A.handle.resume(); // Initial suspend.
|
||||
PRINT ("main: [a_copy = 20, a_ref = 10]");
|
||||
|
||||
int y = A.handle.promise().get_value();
|
||||
if (y != 30)
|
||||
{
|
||||
PRINTF ("main: co-yield = %d, should be 30\n", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
a_copy = 5;
|
||||
a_ref = 7;
|
||||
|
||||
A.handle.resume(); // from the yield.
|
||||
PRINT ("main: [a_copy = 5, a_ref = 7]");
|
||||
|
||||
y = A.handle.promise().get_value();
|
||||
if (y != 27)
|
||||
{
|
||||
PRINTF ("main: co-ret = %d, should be 27\n", y);
|
||||
abort ();
|
||||
}
|
||||
PRINT ("use_lambda: about to return");
|
||||
}
|
||||
~foo () { PRINT ("foo: DTOR"); }
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
foo<int> inst;
|
||||
inst.use_lambda();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
// { dg-do run }
|
||||
|
||||
// The simplest co_await we can do.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
co_await coro1::suspend_always_prt{};
|
||||
co_return gX + 10;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: resuming [1] initial suspend");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [2] co_await");
|
||||
f_coro.handle.resume();
|
||||
/* we should now have returned with the co_return (15) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 11)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 11\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
// { dg-do run }
|
||||
|
||||
/* The simplest valued co_await we can do. */
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
gX = co_await coro1::suspend_always_intprt{};
|
||||
co_return gX + 10;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: resuming [1] initial suspend");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [2] co_await suspend_always_intprt");
|
||||
f_coro.handle.resume();
|
||||
if (gX != 5)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 5\n", gX);
|
||||
abort ();
|
||||
}
|
||||
/* we should now have returned with the co_return (15) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 15)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 15\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
58
gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
Normal file
58
gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
Normal file
@ -0,0 +1,58 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Test of basic await transform, no local state.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
gX = co_await 11;
|
||||
co_return gX + 31;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: resuming [1] initial suspend");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [2] co_await");
|
||||
f_coro.handle.resume();
|
||||
if (gX != 11)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
|
||||
abort ();
|
||||
}
|
||||
/* we should now have returned with the co_return (15) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 42)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 42\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
58
gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
Normal file
58
gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
Normal file
@ -0,0 +1,58 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Basic check of co_await with an expression to await transform.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
gX = co_await 11 + 15;
|
||||
co_return gX + 16;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: resuming [1] initial suspend");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [1] await");
|
||||
f_coro.handle.resume();
|
||||
if (gX != 26)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 26\n", gX);
|
||||
abort ();
|
||||
}
|
||||
/* we should now have returned with the co_return (26+16) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 42)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 42\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check correct operation of await transform.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
/* Valued with an await_transform. */
|
||||
int gX = 1;
|
||||
int y = 30;
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
if (gX < 12) {
|
||||
gX += y;
|
||||
gX += co_await 11;
|
||||
} else
|
||||
gX += co_await 12;
|
||||
|
||||
co_return gX;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: gX OK -- looping");
|
||||
do {
|
||||
PRINTF ("main: gX : %d \n", gX);
|
||||
f_coro.handle.resume();
|
||||
} while (!f_coro.handle.done());
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 42)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 42\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
51
gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
Normal file
51
gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
Normal file
@ -0,0 +1,51 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check correct operation of co_await in a loop without local state.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
/* Valued with an await_transform. */
|
||||
int gX = 1;
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
gX += co_await 11;
|
||||
if (gX > 100)
|
||||
break;
|
||||
}
|
||||
co_return gX;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: gX OK -- looping");
|
||||
do {
|
||||
PRINTF ("main: gX : %d \n", gX);
|
||||
f_coro.handle.resume();
|
||||
} while (!f_coro.handle.done());
|
||||
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
// first value above 100 is 10*11 + 1.
|
||||
if (y != 111)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 111\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
65
gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
Normal file
65
gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
Normal file
@ -0,0 +1,65 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Basic check of the co_await operator overload.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
/* A very simple overload. */
|
||||
struct empty
|
||||
{
|
||||
auto operator co_await() const & noexcept {
|
||||
return coro1::suspend_always_intprt{};
|
||||
}
|
||||
};
|
||||
|
||||
int gX = 1;
|
||||
empty e{};
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
int a = co_await(e); /* operator ovl. */
|
||||
co_return gX + 5 + a;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done'");
|
||||
abort ();
|
||||
}
|
||||
|
||||
PRINT ("main: resuming [1] initial suspend");
|
||||
f_coro.handle.resume();
|
||||
|
||||
PRINT ("main: resuming [2] co_await");
|
||||
f_coro.handle.resume();
|
||||
|
||||
/* we should now have returned with the co_return (11) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 11)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 11\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
132
gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
Normal file
132
gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
Normal file
@ -0,0 +1,132 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check that we correctly operate when the coroutine object is templated.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
template <typename T>
|
||||
struct coro1 {
|
||||
struct promise_type;
|
||||
using handle_type = coro::coroutine_handle<coro1::promise_type>;
|
||||
handle_type handle;
|
||||
coro1 () : handle(0) {}
|
||||
coro1 (handle_type _handle)
|
||||
: handle(_handle) {
|
||||
PRINT ("Created coro1 object from handle");
|
||||
}
|
||||
coro1 (const coro1 &) = delete; // no copying
|
||||
coro1 (coro1 &&s) : handle(s.handle) {
|
||||
s.handle = nullptr;
|
||||
PRINT ("Moved coro1");
|
||||
}
|
||||
coro1 &operator = (coro1 &&s) {
|
||||
handle = s.handle;
|
||||
s.handle = nullptr;
|
||||
return *this;
|
||||
}
|
||||
~coro1() {
|
||||
PRINT ("Destroyed coro1");
|
||||
if ( handle )
|
||||
handle.destroy();
|
||||
}
|
||||
|
||||
struct suspend_never_prt {
|
||||
~suspend_never_prt() {}
|
||||
bool await_ready() const noexcept { return true; }
|
||||
void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
|
||||
void await_resume() const noexcept {PRINT ("susp-never-resume");}
|
||||
};
|
||||
|
||||
struct suspend_always_prt {
|
||||
T x;
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
|
||||
void await_resume() const noexcept {PRINT ("susp-always-resume");}
|
||||
};
|
||||
|
||||
/* This returns the int it was constructed with. */
|
||||
struct suspend_always_intprt {
|
||||
T x;
|
||||
suspend_always_intprt() : x((T)5) { PRINT ("suspend_always_intprt def ctor"); }
|
||||
suspend_always_intprt(T _x) : x(_x)
|
||||
{ PRINTF ("suspend_always_intprt ctor with %ld\n", (long)x); }
|
||||
~suspend_always_intprt() {}
|
||||
bool await_ready() const noexcept { return false; }
|
||||
void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
|
||||
int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;}
|
||||
};
|
||||
|
||||
struct promise_type {
|
||||
T value;
|
||||
promise_type() { PRINT ("Created Promise"); }
|
||||
~promise_type() { PRINT ("Destroyed Promise"); }
|
||||
|
||||
coro1 get_return_object() {
|
||||
PRINT ("get_return_object: from handle from promise");
|
||||
return coro1 (handle_type::from_promise (*this));
|
||||
}
|
||||
|
||||
auto initial_suspend() {
|
||||
PRINT ("get initial_suspend ");
|
||||
return suspend_never_prt{};
|
||||
}
|
||||
|
||||
auto final_suspend() {
|
||||
PRINT ("get final_suspend");
|
||||
return suspend_always_prt{};
|
||||
}
|
||||
|
||||
void return_value (int v) {
|
||||
PRINTF ("return_value () %ld\n", (long) v);
|
||||
value = v;
|
||||
}
|
||||
|
||||
auto await_transform (T v) {
|
||||
PRINTF ("await_transform a T () %ld\n", (long)v);
|
||||
return suspend_always_intprt (v);
|
||||
}
|
||||
|
||||
T get_value () { return value; }
|
||||
void unhandled_exception() { PRINT ("** unhandled exception"); }
|
||||
};
|
||||
};
|
||||
|
||||
/* Valued with an await_transform. */
|
||||
int gX = 2;
|
||||
|
||||
template <typename T>
|
||||
coro1<T> f ()
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
gX += co_await 10;
|
||||
}
|
||||
co_return gX;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
auto f_coro = f<int>();
|
||||
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 2)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 2\n", gX);
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: gX OK -- looping");
|
||||
do {
|
||||
f_coro.handle.resume();
|
||||
} while (!f_coro.handle.done());
|
||||
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
|
||||
if (y != 42)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 42\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check cascaded co_await operations.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
/* Valued with an await_transform. */
|
||||
int gX = 1;
|
||||
coro1 f ()
|
||||
{
|
||||
/* We are going to use an await transform that takes a long, the
|
||||
await_resume squares it.
|
||||
so we get 11 ** 4, 14641. */
|
||||
gX = (int) co_await co_await 11L;
|
||||
co_return gX + 31;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: resuming [1] - inital suspend");
|
||||
f_coro.handle.resume();
|
||||
|
||||
PRINT ("main: resuming [2] - nested");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [3] - outer");
|
||||
f_coro.handle.resume();
|
||||
|
||||
if (gX != 14641)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 14641\n", gX);
|
||||
abort ();
|
||||
}
|
||||
/* we should now have returned with the co_return (14672) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 14672)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 14672\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
57
gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
Normal file
57
gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
Normal file
@ -0,0 +1,57 @@
|
||||
// { dg-do run }
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
/* Valued with an await_transform. */
|
||||
int gX = 1;
|
||||
coro1 f ()
|
||||
{
|
||||
gX = co_await 11 + co_await 15;
|
||||
co_return gX + 31;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: resuming [1] (initial suspend)");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [2] one side of add");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [3] other side of add");
|
||||
f_coro.handle.resume();
|
||||
if (gX != 26)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 26\n", gX);
|
||||
abort ();
|
||||
}
|
||||
/* we should now have returned with the co_return (57) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 57)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 57\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Check type dependent function parms.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
// there is a promise ctor that takes a single int.
|
||||
#define USE_AWAIT_TRANSFORM
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
template <typename T>
|
||||
coro1
|
||||
f (T y) noexcept
|
||||
{
|
||||
PRINT ("coro1: about to return");
|
||||
T x = y;
|
||||
co_return co_await x + 3;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 x = f<int>(17);
|
||||
|
||||
/* We should have created the promise with an initial value of
|
||||
17. */
|
||||
int y = x.handle.promise().get_value();
|
||||
if ( y != 17 )
|
||||
{
|
||||
PRINTF ("main: wrong promise init (%d).", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
PRINT ("main: got coro1 - resuming");
|
||||
if (x.handle.done())
|
||||
abort();
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (initial suspend)");
|
||||
|
||||
x.handle.resume();
|
||||
PRINT ("main: after resume (co_await)");
|
||||
|
||||
/* Now we should have the co_returned value. */
|
||||
y = x.handle.promise().get_value();
|
||||
if ( y != 20 )
|
||||
{
|
||||
PRINTF ("main: wrong result (%d).", y);
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (!x.handle.done())
|
||||
{
|
||||
PRINT ("main: apparently not done...");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: returning");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Test of forwarding a templated awaitable to co_await.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
/* Valued with an await_transform. */
|
||||
|
||||
template< typename AWAITABLE >
|
||||
coro1
|
||||
test_fwd (AWAITABLE&& awaitable)
|
||||
{
|
||||
// the await_resume() just returns the saved int value.
|
||||
int a = co_await std::forward<AWAITABLE>(awaitable);
|
||||
// Which we co-return to the promise so that it can be
|
||||
// retrieved.
|
||||
co_return a;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
// We have an awaitable that stores the int it was constructed with.
|
||||
coro1::suspend_always_intprt g(15);
|
||||
struct coro1 g_coro = test_fwd (g);
|
||||
|
||||
PRINT ("main: resuming g [1] (initial suspend)");
|
||||
g_coro.handle.resume();
|
||||
|
||||
PRINT ("main: resuming g [2] co_await");
|
||||
g_coro.handle.resume();
|
||||
|
||||
int y = g_coro.handle.promise().get_value();
|
||||
if (y != 15)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 15\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
// { dg-do run }
|
||||
|
||||
// Basic check of the co_await operator overload.
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
/* A very simple overload. */
|
||||
struct empty
|
||||
{
|
||||
auto operator co_await() & noexcept {
|
||||
return coro1::suspend_always_intprt{};
|
||||
}
|
||||
auto operator co_await() && noexcept {
|
||||
return coro1::suspend_always_longprtsq(3L);
|
||||
}
|
||||
};
|
||||
|
||||
empty e{};
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
int a = co_await e; /* operator ovl lv. */
|
||||
int b = co_await empty{}; /* operator ovl rv. */
|
||||
co_return b + a;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done'");
|
||||
abort ();
|
||||
}
|
||||
|
||||
PRINT ("main: resuming [1] initial suspend");
|
||||
f_coro.handle.resume();
|
||||
|
||||
PRINT ("main: resuming [2] co_await a");
|
||||
f_coro.handle.resume();
|
||||
|
||||
PRINT ("main: resuming [3] co_await b");
|
||||
f_coro.handle.resume();
|
||||
|
||||
/* we should now have returned with the co_return (14) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 14)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 14\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
// { dg-do run }
|
||||
|
||||
/* The simplest valued co_await we can do. */
|
||||
|
||||
#include "../coro.h"
|
||||
|
||||
// boiler-plate for tests of codegen
|
||||
#include "../coro1-ret-int-yield-int.h"
|
||||
|
||||
int gX = 1;
|
||||
|
||||
coro1
|
||||
f ()
|
||||
{
|
||||
int t = 5;
|
||||
gX = co_await coro1::suspend_always_intrefprt{t};
|
||||
co_return t + 10;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
PRINT ("main: create coro1");
|
||||
struct coro1 f_coro = f ();
|
||||
PRINT ("main: got coro1 - checking gX");
|
||||
if (gX != 1)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
|
||||
abort ();
|
||||
}
|
||||
if (f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should not be 'done' [1]");
|
||||
abort ();
|
||||
}
|
||||
PRINT ("main: resuming [1] initial suspend");
|
||||
f_coro.handle.resume();
|
||||
PRINT ("main: resuming [2] co_await suspend_always_intprt");
|
||||
f_coro.handle.resume();
|
||||
if (gX != 5)
|
||||
{
|
||||
PRINTF ("main: gX is wrong : %d, should be 5\n", gX);
|
||||
abort ();
|
||||
}
|
||||
/* we should now have returned with the co_return (15) */
|
||||
if (!f_coro.handle.done())
|
||||
{
|
||||
PRINT ("main: we should be 'done' ");
|
||||
abort ();
|
||||
}
|
||||
int y = f_coro.handle.promise().get_value();
|
||||
if (y != 15)
|
||||
{
|
||||
PRINTF ("main: y is wrong : %d, should be 15\n", y);
|
||||
abort ();
|
||||
}
|
||||
puts ("main: done");
|
||||
return 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user