diff mbox series

c++: Improve diagnostics for constexpr cast from void*

Message ID 6523cffe.170a0220.653b.72b9@mx.google.com
State New
Headers show
Series c++: Improve diagnostics for constexpr cast from void* | expand

Commit Message

Nathaniel Shead Oct. 9, 2023, 10:03 a.m. UTC
Bootstrapped and regtested on x86_64-pc-linux-gnu with 
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26,impcx.

-- >8 --

This patch improves the errors given when casting from void* in C++26 to
include the expected type if the type of the pointed-to object was
not similar to the casted-to type. 

It also ensures (for all standard modes) that void* casts are checked
even for DECL_ARTIFICIAL declarations, such as lifetime-extended
temporaries, and is only ignored for cases where we know it's OK (heap
identifiers and source_location::current). This provides more accurate
diagnostics when using the pointer and ensures that some other casts
from void* are now correctly rejected.

gcc/cp/ChangeLog:

	* constexpr.cc (is_std_source_location_current): New.
	(cxx_eval_constant_expression): Only ignore cast from void* for
	specific cases and improve other diagnostics.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/constexpr-cast4.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/constexpr.cc                          | 83 +++++++++++++++++---
 gcc/testsuite/g++.dg/cpp0x/constexpr-cast4.C |  7 ++
 2 files changed, 78 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-cast4.C

Comments

Jason Merrill Oct. 9, 2023, 8:10 p.m. UTC | #1
On 10/9/23 06:03, Nathaniel Shead wrote:
> Bootstrapped and regtested on x86_64-pc-linux-gnu with
> GXX_TESTSUITE_STDS=98,11,14,17,20,23,26,impcx.
> 
> -- >8 --
> 
> This patch improves the errors given when casting from void* in C++26 to
> include the expected type if the type of the pointed-to object was
> not similar to the casted-to type.
> 
> It also ensures (for all standard modes) that void* casts are checked
> even for DECL_ARTIFICIAL declarations, such as lifetime-extended
> temporaries, and is only ignored for cases where we know it's OK (heap
> identifiers and source_location::current). This provides more accurate
> diagnostics when using the pointer and ensures that some other casts
> from void* are now correctly rejected.
> 
> gcc/cp/ChangeLog:
> 
> 	* constexpr.cc (is_std_source_location_current): New.
> 	(cxx_eval_constant_expression): Only ignore cast from void* for
> 	specific cases and improve other diagnostics.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp0x/constexpr-cast4.C: New test.
> 
> Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
> ---
>   gcc/cp/constexpr.cc                          | 83 +++++++++++++++++---
>   gcc/testsuite/g++.dg/cpp0x/constexpr-cast4.C |  7 ++
>   2 files changed, 78 insertions(+), 12 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-cast4.C
> 
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index 0f948db7c2d..f38d541a662 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -2301,6 +2301,36 @@ is_std_allocator_allocate (const constexpr_call *call)
>   	  && is_std_allocator_allocate (call->fundef->decl));
>   }
>   
> +/* Return true if FNDECL is std::source_location::current.  */
> +
> +static inline bool
> +is_std_source_location_current (tree fndecl)
> +{
> +  if (!decl_in_std_namespace_p (fndecl))
> +    return false;
> +
> +  tree name = DECL_NAME (fndecl);
> +  if (name == NULL_TREE || !id_equal (name, "current"))
> +    return false;
> +
> +  tree ctx = DECL_CONTEXT (fndecl);
> +  if (ctx == NULL_TREE || !CLASS_TYPE_P (ctx) || !TYPE_MAIN_DECL (ctx))
> +    return false;
> +
> +  name = DECL_NAME (TYPE_MAIN_DECL (ctx));
> +  return name && id_equal (name, "source_location");
> +}
> +
> +/* Overload for the above taking constexpr_call*.  */
> +
> +static inline bool
> +is_std_source_location_current (const constexpr_call *call)
> +{
> +  return (call
> +	  && call->fundef
> +	  && is_std_source_location_current (call->fundef->decl));
> +}
> +
>   /* Return true if FNDECL is __dynamic_cast.  */
>   
>   static inline bool
> @@ -7850,33 +7880,62 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
>   	if (TYPE_PTROB_P (type)
>   	    && TYPE_PTR_P (TREE_TYPE (op))
>   	    && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (op)))
> -	    /* Inside a call to std::construct_at or to
> -	       std::allocator<T>::{,de}allocate, we permit casting from void*
> +	    /* Inside a call to std::construct_at,
> +	       std::allocator<T>::{,de}allocate, or
> +	       std::source_location::current, we permit casting from void*
>   	       because that is compiler-generated code.  */
>   	    && !is_std_construct_at (ctx->call)
> -	    && !is_std_allocator_allocate (ctx->call))
> +	    && !is_std_allocator_allocate (ctx->call)
> +	    && !is_std_source_location_current (ctx->call))
>   	  {
>   	    /* Likewise, don't error when casting from void* when OP is
>   	       &heap uninit and similar.  */
>   	    tree sop = tree_strip_nop_conversions (op);
> -	    if (TREE_CODE (sop) == ADDR_EXPR
> -		&& VAR_P (TREE_OPERAND (sop, 0))
> -		&& DECL_ARTIFICIAL (TREE_OPERAND (sop, 0)))
> +	    tree decl = NULL_TREE;
> +	    if (TREE_CODE (sop) == ADDR_EXPR)
> +	      decl = TREE_OPERAND (sop, 0);
> +	    if (decl
> +		&& VAR_P (decl)
> +		&& DECL_ARTIFICIAL (decl)
> +		&& (DECL_NAME (decl) == heap_identifier
> +		    || DECL_NAME (decl) == heap_uninit_identifier
> +		    || DECL_NAME (decl) == heap_vec_identifier
> +		    || DECL_NAME (decl) == heap_vec_uninit_identifier))
>   	      /* OK */;
>   	    /* P2738 (C++26): a conversion from a prvalue P of type "pointer to
>   	       cv void" to a pointer-to-object type T unless P points to an
>   	       object whose type is similar to T.  */
> -	    else if (cxx_dialect > cxx23
> -		     && (sop = cxx_fold_indirect_ref (ctx, loc,
> -						      TREE_TYPE (type), sop)))
> +	    else if (cxx_dialect > cxx23)
>   	      {
> -		r = build1 (ADDR_EXPR, type, sop);
> -		break;
> +		r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop);
> +		if (r)
> +		  {
> +		    r = build1 (ADDR_EXPR, type, r);
> +		    break;
> +		  }
> +		if (!ctx->quiet)
> +		  {
> +		    if (TREE_CODE (sop) == ADDR_EXPR)
> +		      {
> +			error_at (loc, "cast from %qT is not allowed because "
> +				  "pointed-to type %qT is not similar to %qT",
> +				  TREE_TYPE (op), TREE_TYPE (TREE_TYPE (sop)),
> +				  TREE_TYPE (type));
> +			tree obj = build_fold_indirect_ref (sop);
> +			inform (DECL_SOURCE_LOCATION (obj),
> +				"pointed-to object declared here");
> +		      }
> +		    else
> +		      error_at (loc, "cast from %qT is not allowed",

Can this be more helpful as well, i.e. say because op is not the address 
of an object of similar type?

Can we only get here if op is null, since we would have returned already 
for non-constant op?

Jason
diff mbox series

Patch

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 0f948db7c2d..f38d541a662 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -2301,6 +2301,36 @@  is_std_allocator_allocate (const constexpr_call *call)
 	  && is_std_allocator_allocate (call->fundef->decl));
 }
 
+/* Return true if FNDECL is std::source_location::current.  */
+
+static inline bool
+is_std_source_location_current (tree fndecl)
+{
+  if (!decl_in_std_namespace_p (fndecl))
+    return false;
+
+  tree name = DECL_NAME (fndecl);
+  if (name == NULL_TREE || !id_equal (name, "current"))
+    return false;
+
+  tree ctx = DECL_CONTEXT (fndecl);
+  if (ctx == NULL_TREE || !CLASS_TYPE_P (ctx) || !TYPE_MAIN_DECL (ctx))
+    return false;
+
+  name = DECL_NAME (TYPE_MAIN_DECL (ctx));
+  return name && id_equal (name, "source_location");
+}
+
+/* Overload for the above taking constexpr_call*.  */
+
+static inline bool
+is_std_source_location_current (const constexpr_call *call)
+{
+  return (call
+	  && call->fundef
+	  && is_std_source_location_current (call->fundef->decl));
+}
+
 /* Return true if FNDECL is __dynamic_cast.  */
 
 static inline bool
@@ -7850,33 +7880,62 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	if (TYPE_PTROB_P (type)
 	    && TYPE_PTR_P (TREE_TYPE (op))
 	    && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (op)))
-	    /* Inside a call to std::construct_at or to
-	       std::allocator<T>::{,de}allocate, we permit casting from void*
+	    /* Inside a call to std::construct_at,
+	       std::allocator<T>::{,de}allocate, or
+	       std::source_location::current, we permit casting from void*
 	       because that is compiler-generated code.  */
 	    && !is_std_construct_at (ctx->call)
-	    && !is_std_allocator_allocate (ctx->call))
+	    && !is_std_allocator_allocate (ctx->call)
+	    && !is_std_source_location_current (ctx->call))
 	  {
 	    /* Likewise, don't error when casting from void* when OP is
 	       &heap uninit and similar.  */
 	    tree sop = tree_strip_nop_conversions (op);
-	    if (TREE_CODE (sop) == ADDR_EXPR
-		&& VAR_P (TREE_OPERAND (sop, 0))
-		&& DECL_ARTIFICIAL (TREE_OPERAND (sop, 0)))
+	    tree decl = NULL_TREE;
+	    if (TREE_CODE (sop) == ADDR_EXPR)
+	      decl = TREE_OPERAND (sop, 0);
+	    if (decl
+		&& VAR_P (decl)
+		&& DECL_ARTIFICIAL (decl)
+		&& (DECL_NAME (decl) == heap_identifier
+		    || DECL_NAME (decl) == heap_uninit_identifier
+		    || DECL_NAME (decl) == heap_vec_identifier
+		    || DECL_NAME (decl) == heap_vec_uninit_identifier))
 	      /* OK */;
 	    /* P2738 (C++26): a conversion from a prvalue P of type "pointer to
 	       cv void" to a pointer-to-object type T unless P points to an
 	       object whose type is similar to T.  */
-	    else if (cxx_dialect > cxx23
-		     && (sop = cxx_fold_indirect_ref (ctx, loc,
-						      TREE_TYPE (type), sop)))
+	    else if (cxx_dialect > cxx23)
 	      {
-		r = build1 (ADDR_EXPR, type, sop);
-		break;
+		r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop);
+		if (r)
+		  {
+		    r = build1 (ADDR_EXPR, type, r);
+		    break;
+		  }
+		if (!ctx->quiet)
+		  {
+		    if (TREE_CODE (sop) == ADDR_EXPR)
+		      {
+			error_at (loc, "cast from %qT is not allowed because "
+				  "pointed-to type %qT is not similar to %qT",
+				  TREE_TYPE (op), TREE_TYPE (TREE_TYPE (sop)),
+				  TREE_TYPE (type));
+			tree obj = build_fold_indirect_ref (sop);
+			inform (DECL_SOURCE_LOCATION (obj),
+				"pointed-to object declared here");
+		      }
+		    else
+		      error_at (loc, "cast from %qT is not allowed",
+				TREE_TYPE (op));
+		  }
+		*non_constant_p = true;
+		return t;
 	      }
 	    else
 	      {
 		if (!ctx->quiet)
-		  error_at (loc, "cast from %qT is not allowed",
+		  error_at (loc, "cast from %qT is not allowed before C++26",
 			    TREE_TYPE (op));
 		*non_constant_p = true;
 		return t;
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-cast4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast4.C
new file mode 100644
index 00000000000..0e7fd87add8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast4.C
@@ -0,0 +1,7 @@ 
+// { dg-do compile { target c++11 } }
+
+constexpr int&& r = 1 + 2;  // { dg-message "pointed-to object declared here" "" { target c++26 } }
+constexpr void *vpr = &r;
+constexpr int* pi = static_cast<int *>(vpr);  // { dg-error "cast from .void\\*. is not allowed" "" { target c++23_down } }
+constexpr float* pf = static_cast<float *>(vpr);  // { dg-error "cast from .void\\*. is not allowed" "" { target c++23_down } }
+// { dg-error "cast from .void\\*. is not allowed because pointed-to type .int. is not similar to .float." "" { target c++26 } .-1 }