This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: C++ PATCH for c++/91346 - Implement C++2a P1668R1, allow unevaluated asm in constexpr
- From: Jason Merrill <jason at redhat dot com>
- To: Marek Polacek <polacek at redhat dot com>
- Cc: gcc-patches List <gcc-patches at gcc dot gnu dot org>
- Date: Tue, 6 Aug 2019 21:25:45 -0400
- Subject: Re: C++ PATCH for c++/91346 - Implement C++2a P1668R1, allow unevaluated asm in constexpr
- References: <20190806142811.GG28284@redhat.com>
Let's downgrade the errors in earlier standard modes to pedwarn. Ok with
that change.
On Tue, Aug 6, 2019, 10:28 AM Marek Polacek <polacek@redhat.com> wrote:
> This patch implements another C++2a feature, P1668R1: Permit unevaluated
> inline
> asm in constexpr functions.
>
> It's really straightforward so not much to say; we need to allow ASM_EXPRs
> in
> potential_constant_expression_1, but since only unevaluated asm is allowed,
> cxx_eval_constant_expression must give an error when it encounters an
> ASM_EXPR.
>
> I've improved location for "asm", so that the error points to "asm" rather
> than
> to ";".
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2019-08-06 Marek Polacek <polacek@redhat.com>
>
> PR c++/91346 - Implement P1668R1, allow unevaluated asm in
> constexpr.
> * constexpr.c (cxx_eval_constant_expression): Handle ASM_EXPR.
> (potential_constant_expression_1) <case ASM_EXPR>: Allow in C++2a,
> give
> an error otherwise.
> * cp-tree.h (finish_asm_stmt): Adjust.
> * parser.c (cp_parser_asm_definition): Grab the locaion of "asm"
> and
> use it. Adjust an error message. Allow asm in C++2a.
> * pt.c (tsubst_expr): Pass a location down to finish_asm_stmt.
> * semantics.c (finish_asm_stmt): New location_t parameter. Use it.
>
> * g++.dg/cpp2a/inline-asm1.C: New test.
> * g++.dg/cpp2a/inline-asm2.C: New test.
> * g++.dg/cpp1y/constexpr-neg1.C: Adjust dg-error.
>
> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
> index 36a66337433..d45e65df400 100644
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -5289,6 +5289,18 @@ cxx_eval_constant_expression (const constexpr_ctx
> *ctx, tree t,
> r = void_node;
> break;
>
> + case ASM_EXPR:
> + if (!ctx->quiet)
> + {
> + error_at (cp_expr_loc_or_input_loc (t),
> + "inline assembly is not a constant expression");
> + inform (cp_expr_loc_or_input_loc (t),
> + "only unevaluated inline assembly is allowed in a "
> + "%<constexpr%> function in C++2a");
> + }
> + *non_constant_p = true;
> + return t;
> +
> default:
> if (STATEMENT_CODE_P (TREE_CODE (t)))
> {
> @@ -6469,13 +6481,22 @@ potential_constant_expression_1 (tree t, bool
> want_rval, bool strict, bool now,
> /* GCC internal stuff. */
> case VA_ARG_EXPR:
> case TRANSACTION_EXPR:
> - case ASM_EXPR:
> case AT_ENCODE_EXPR:
> fail:
> if (flags & tf_error)
> error_at (loc, "expression %qE is not a constant expression", t);
> return false;
>
> + case ASM_EXPR:
> + if (cxx_dialect >= cxx2a)
> + /* In C++2a, unevaluated inline assembly is permitted in constexpr
> + functions. */
> + return true;
> + else if (flags & tf_error)
> + error_at (loc, "inline assembly is not allowed in %<constexpr%> "
> + "functions before C++2a");
> + return false;
> +
> case OBJ_TYPE_REF:
> if (cxx_dialect >= cxx2a)
> /* In C++2a virtual calls can be constexpr, don't give up yet. */
> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> index d4e67cdfd96..72ee1d61e97 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -7052,8 +7052,8 @@ enum {
> extern tree begin_compound_stmt (unsigned int);
>
> extern void finish_compound_stmt (tree);
> -extern tree finish_asm_stmt (int, tree, tree, tree,
> tree,
> - tree, bool);
> +extern tree finish_asm_stmt (location_t, int, tree,
> tree,
> + tree, tree, tree, bool);
> extern tree finish_label_stmt (tree);
> extern void finish_label_decl (tree);
> extern cp_expr finish_parenthesized_expr (cp_expr);
> diff --git gcc/cp/parser.c gcc/cp/parser.c
> index 79da7b52eb9..2d0e6a0c931 100644
> --- gcc/cp/parser.c
> +++ gcc/cp/parser.c
> @@ -19817,14 +19817,19 @@ cp_parser_asm_definition (cp_parser* parser)
> bool invalid_inputs_p = false;
> bool invalid_outputs_p = false;
> required_token missing = RT_NONE;
> + location_t asm_loc = cp_lexer_peek_token (parser->lexer)->location;
>
> /* Look for the `asm' keyword. */
> cp_parser_require_keyword (parser, RID_ASM, RT_ASM);
>
> if (parser->in_function_body
> - && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
> + && DECL_DECLARED_CONSTEXPR_P (current_function_decl)
> + /* In C++2a, unevaluated inline assembly is permitted in constexpr
> + functions. */
> + && (cxx_dialect < cxx2a))
> {
> - error ("%<asm%> in %<constexpr%> function");
> + error_at (asm_loc, "%<asm%> in %<constexpr%> function only
> available "
> + "with %<-std=c++2a%> or %<-std=gnu++2a%>");
> cp_function_chain->invalid_constexpr = true;
> }
>
> @@ -20032,7 +20037,7 @@ cp_parser_asm_definition (cp_parser* parser)
> /* Create the ASM_EXPR. */
> if (parser->in_function_body)
> {
> - asm_stmt = finish_asm_stmt (volatile_p, string, outputs,
> + asm_stmt = finish_asm_stmt (asm_loc, volatile_p, string, outputs,
> inputs, clobbers, labels, inline_p);
> /* If the extended syntax was not used, mark the ASM_EXPR. */
> if (!extended_p)
> diff --git gcc/cp/pt.c gcc/cp/pt.c
> index 903e589b663..85cff96c1b6 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -17394,8 +17394,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
> complain, tree in_decl,
> complain, in_decl);
> tree labels = tsubst_copy_asm_operands (ASM_LABELS (t), args,
> complain, in_decl);
> - tmp = finish_asm_stmt (ASM_VOLATILE_P (t), string, outputs, inputs,
> - clobbers, labels, ASM_INLINE_P (t));
> + tmp = finish_asm_stmt (EXPR_LOCATION (t), ASM_VOLATILE_P (t),
> string,
> + outputs, inputs, clobbers, labels,
> + ASM_INLINE_P (t));
> tree asm_expr = tmp;
> if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR)
> asm_expr = TREE_OPERAND (asm_expr, 0);
> diff --git gcc/cp/semantics.c gcc/cp/semantics.c
> index fa6962454bf..83dc122e0bd 100644
> --- gcc/cp/semantics.c
> +++ gcc/cp/semantics.c
> @@ -1484,8 +1484,9 @@ finish_compound_stmt (tree stmt)
> considered volatile, and whether it is asm inline. */
>
> tree
> -finish_asm_stmt (int volatile_p, tree string, tree output_operands,
> - tree input_operands, tree clobbers, tree labels, bool
> inline_p)
> +finish_asm_stmt (location_t loc, int volatile_p, tree string,
> + tree output_operands, tree input_operands, tree clobbers,
> + tree labels, bool inline_p)
> {
> tree r;
> tree t;
> @@ -1532,7 +1533,7 @@ finish_asm_stmt (int volatile_p, tree string, tree
> output_operands,
> effectively const. */
> || (CLASS_TYPE_P (TREE_TYPE (operand))
> && C_TYPE_FIELDS_READONLY (TREE_TYPE (operand)))))
> - cxx_readonly_error (input_location, operand, lv_asm);
> + cxx_readonly_error (loc, operand, lv_asm);
>
> tree *op = &operand;
> while (TREE_CODE (*op) == COMPOUND_EXPR)
> @@ -1585,8 +1586,9 @@ finish_asm_stmt (int volatile_p, tree string, tree
> output_operands,
> resolve the overloading. */
> if (TREE_TYPE (operand) == unknown_type_node)
> {
> - error ("type of %<asm%> operand %qE could not be determined",
> - TREE_VALUE (t));
> + error_at (loc,
> + "type of %<asm%> operand %qE could not be
> determined",
> + TREE_VALUE (t));
> operand = error_mark_node;
> }
>
> @@ -1634,7 +1636,7 @@ finish_asm_stmt (int volatile_p, tree string, tree
> output_operands,
> }
> }
>
> - r = build_stmt (input_location, ASM_EXPR, string,
> + r = build_stmt (loc, ASM_EXPR, string,
> output_operands, input_operands,
> clobbers, labels);
> ASM_VOLATILE_P (r) = volatile_p || noutputs == 0;
> diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> index ae3dcc69cf0..d82dbada1bf 100644
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
> @@ -7,7 +7,7 @@ constexpr int f(int i) {
> thread_local int l = i; // { dg-error "thread_local" }
> goto foo; // { dg-error "goto" }
> foo:
> - asm("foo"); // { dg-error "asm" }
> + asm("foo"); // { dg-error "asm" "" { target c++17_down
> } }
> int k; // { dg-error "uninitialized" }
> A a; // { dg-error "non-literal" }
> return i;
> diff --git gcc/testsuite/g++.dg/cpp2a/inline-asm1.C
> gcc/testsuite/g++.dg/cpp2a/inline-asm1.C
> new file mode 100644
> index 00000000000..9ac588f0ecc
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/inline-asm1.C
> @@ -0,0 +1,12 @@
> +// P1668R1: Permit unevaluated inline asm in constexpr functions
> +// PR c++/91346
> +// { dg-do compile { target c++11 } }
> +
> +constexpr int
> +foo (int a, int b)
> +{
> + if (__builtin_is_constant_evaluated ())
> + return a + b;
> + asm (""); // { dg-error ".asm. in .constexpr. function only available
> with" "" { target c++17_down } }
> + return a;
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/inline-asm2.C
> gcc/testsuite/g++.dg/cpp2a/inline-asm2.C
> new file mode 100644
> index 00000000000..6038c111eb0
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/inline-asm2.C
> @@ -0,0 +1,17 @@
> +// P1668R1: Permit unevaluated inline asm in constexpr functions
> +// PR c++/91346
> +// { dg-do compile { target c++2a } }
> +
> +constexpr int
> +foo (bool b)
> +{
> + if (b)
> + return 42;
> + asm (""); // { dg-error "inline assembly is not a constant expression" }
> +// { dg-message "only unevaluated inline assembly" "" { target *-*-* }
> .-1 }
> + return -1;
> +}
> +
> +constexpr int i = foo (true);
> +static_assert(i == 42, "");
> +constexpr int j = foo (false); // { dg-message "in .constexpr. expansion
> of" }
>