Message ID | 20190327190837.GN26967@redhat.com |
---|---|
State | New |
Headers | show |
Series | C++ PATCH for c++/89836 - bool constant expression and explicit conversions | expand |
On 3/27/19 3:08 PM, Marek Polacek wrote: > I noticed that this test doesn't compile because build_converted_constant_expr > failed to consider explicit conversion functions. That's wrong: while Core > Issue 1981 specifies that explicit conversion functions are not considered for > contextual conversions, they are considered in contextual conversions to bool, > as defined in Core 2039. > > Fixed by adding a new wrapper that uses LOOKUP_NORMAL instead of > LOOKUP_IMPLICIT. > > Besides [dcl.fct.spec], these: [except.spec], [dcl.dcl]/6 (static_assert), > [stmt.if] (constexpr if) all talk about "a contextually converted constant > expression of type bool", so it would seem to make sense to use > build_converted_constant_bool_expr for them also. E.g. use that instead of > perform_implicit_conversion_flags in build_noexcept_spec. But that doesn't > work for this test: > > int e(); > int fn() noexcept(e); > > because build_converted_constant_expr would issue a conversion error. We're > converting "e" to "bool". We have a ck_lvalue conversion from "int f()" to > "int (*f)()" and then a ck_std conversion from "int (*f)()" to bool. Those > should work fine but we issue an error for the ck_std conversion. Converting a pointer to bool is a "boolean conversion", which is not allowed under the rules for a converted constant expression ([expr.const]p7). So we should reject that testcase. > Also build_converted_constant_expr doesn't have the processing_template_decl > handling that perform_implicit_conversion_flags does, which (I believe) lead me > to changing check_narrowing to use maybe_constant_value instead of > fold_non_dependent_expr. As I was saying elsewhere, I think that change was probably a mistake, and that we should have fixed that bug by changing the caller instead. > Anyway, this patch should be safe. > > Bootstrapped/regtested on x86_64-linux, ok for trunk? OK. Jason
On Thu, Mar 28, 2019 at 02:02:47PM -0400, Jason Merrill wrote: > On 3/27/19 3:08 PM, Marek Polacek wrote: > > I noticed that this test doesn't compile because build_converted_constant_expr > > failed to consider explicit conversion functions. That's wrong: while Core > > Issue 1981 specifies that explicit conversion functions are not considered for > > contextual conversions, they are considered in contextual conversions to bool, > > as defined in Core 2039. > > > > Fixed by adding a new wrapper that uses LOOKUP_NORMAL instead of > > LOOKUP_IMPLICIT. > > > > Besides [dcl.fct.spec], these: [except.spec], [dcl.dcl]/6 (static_assert), > > [stmt.if] (constexpr if) all talk about "a contextually converted constant > > expression of type bool", so it would seem to make sense to use > > build_converted_constant_bool_expr for them also. E.g. use that instead of > > perform_implicit_conversion_flags in build_noexcept_spec. But that doesn't > > work for this test: > > > > int e(); > > int fn() noexcept(e); > > > > because build_converted_constant_expr would issue a conversion error. We're > > converting "e" to "bool". We have a ck_lvalue conversion from "int f()" to > > "int (*f)()" and then a ck_std conversion from "int (*f)()" to bool. Those > > should work fine but we issue an error for the ck_std conversion. > > Converting a pointer to bool is a "boolean conversion", which is not allowed > under the rules for a converted constant expression ([expr.const]p7). So we > should reject that testcase. Oh, right (though clang also accepts it). I'll see if there's anything else that breaks if I use build_converted_constant_expr instead of perform_implicit_conversion. > > Also build_converted_constant_expr doesn't have the processing_template_decl > > handling that perform_implicit_conversion_flags does, which (I believe) lead me > > to changing check_narrowing to use maybe_constant_value instead of > > fold_non_dependent_expr. > > As I was saying elsewhere, I think that change was probably a mistake, and > that we should have fixed that bug by changing the caller instead. I need to address that. Unfortunately adding a sentinel in the caller broke so much stuff :(. > > Anyway, this patch should be safe. > > > > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > OK. Thanks, committed. Marek
diff --git gcc/cp/call.c gcc/cp/call.c index 9efca735b16..bc5179416a5 100644 --- gcc/cp/call.c +++ gcc/cp/call.c @@ -4175,18 +4175,11 @@ build_user_type_conversion (tree totype, tree expr, int flags, return ret; } -/* Subroutine of convert_nontype_argument. - - EXPR is an expression used in a context that requires a converted - constant-expression, such as a template non-type parameter. Do any - necessary conversions (that are permitted for converted - constant-expressions) to convert it to the desired type. - - If conversion is successful, returns the converted expression; - otherwise, returns error_mark_node. */ +/* Worker for build_converted_constant_expr. */ -tree -build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain) +static tree +build_converted_constant_expr_internal (tree type, tree expr, + int flags, tsubst_flags_t complain) { conversion *conv; void *p; @@ -4200,8 +4193,7 @@ build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain) p = conversion_obstack_alloc (0); conv = implicit_conversion (type, TREE_TYPE (expr), expr, - /*c_cast_p=*/false, - LOOKUP_IMPLICIT, complain); + /*c_cast_p=*/false, flags, complain); /* A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant @@ -4304,6 +4296,38 @@ build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain) return expr; } +/* Subroutine of convert_nontype_argument. + + EXPR is an expression used in a context that requires a converted + constant-expression, such as a template non-type parameter. Do any + necessary conversions (that are permitted for converted + constant-expressions) to convert it to the desired type. + + This function doesn't consider explicit conversion functions. If + you mean to use "a contextually converted constant expression of type + bool", use build_converted_constant_bool_expr. + + If conversion is successful, returns the converted expression; + otherwise, returns error_mark_node. */ + +tree +build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain) +{ + return build_converted_constant_expr_internal (type, expr, LOOKUP_IMPLICIT, + complain); +} + +/* Used to create "a contextually converted constant expression of type + bool". This differs from build_converted_constant_expr in that it + also considers explicit conversion functions. */ + +tree +build_converted_constant_bool_expr (tree expr, tsubst_flags_t complain) +{ + return build_converted_constant_expr_internal (boolean_type_node, expr, + LOOKUP_NORMAL, complain); +} + /* Do any initial processing on the arguments to a function call. */ static vec<tree, va_gc> * diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h index 3fe91ad2160..fd612b0dbb1 100644 --- gcc/cp/cp-tree.h +++ gcc/cp/cp-tree.h @@ -6233,6 +6233,7 @@ extern int remaining_arguments (tree); extern tree perform_implicit_conversion (tree, tree, tsubst_flags_t); extern tree perform_implicit_conversion_flags (tree, tree, tsubst_flags_t, int); extern tree build_converted_constant_expr (tree, tree, tsubst_flags_t); +extern tree build_converted_constant_bool_expr (tree, tsubst_flags_t); extern tree perform_direct_initialization_if_possible (tree, tree, bool, tsubst_flags_t); extern tree in_charge_arg_for_name (tree); diff --git gcc/cp/decl.c gcc/cp/decl.c index c8435e29491..c46a39665bd 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -16700,7 +16700,7 @@ build_explicit_specifier (tree expr, tsubst_flags_t complain) expr = instantiate_non_dependent_expr_sfinae (expr, complain); /* Don't let convert_like_real create more template codes. */ processing_template_decl_sentinel s; - expr = build_converted_constant_expr (boolean_type_node, expr, complain); + expr = build_converted_constant_bool_expr (expr, complain); expr = cxx_constant_value (expr); return expr; } diff --git gcc/testsuite/g++.dg/cpp2a/explicit15.C gcc/testsuite/g++.dg/cpp2a/explicit15.C new file mode 100644 index 00000000000..e0058f6dc87 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/explicit15.C @@ -0,0 +1,10 @@ +// PR c++/89836 +// { dg-do compile { target c++2a } } + +struct W { + constexpr explicit operator bool() { return true; }; +}; + +struct U { + explicit(W()) U(int); +};