[RFC] c++/96765: warn when casting Base* to Derived* in Base ctor/dtor

Message ID CALHvHFUht0DAxjCWWsigDzdg39mw-Bv6oEKVbg5YPNrYD9oLZg@mail.gmail.com
State New
Headers
Series [RFC] c++/96765: warn when casting Base* to Derived* in Base ctor/dtor |

Commit Message

Zhao Wei Liew Feb. 23, 2022, 2:57 a.m. UTC
  Hi!

This patch aims to add a warning when casting "this" in a base class
constructor to a derived class type. It works on the test cases
provided, but I'm still running regression tests.

However, I have a few doubts:
1. Am I missing out any cases? Right now, I'm identifying the casts by
checking that TREE_CODE (expr) == NOP_EXPR && is_this_parameter
(TREE_OPERAND (expr, 0)). It seems fine to me but perhaps there is a
function that I can use to express this more concisely?
2. -Wcast-qual doesn't seem to be the right flag for this warning.
However, I can't seem to find an appropriate flag. Maybe I should
place it under -Wextra or -Wall?

Appreciate any feedback on the aforementioned doubts or otherwise.
Thanks, and have a great day!
From 8a1f352f3db06faf264bc823387714a4a9e638b6 Mon Sep 17 00:00:00 2001
From: Zhao Wei Liew <zhaoweiliew@gmail.com>
Date: Tue, 22 Feb 2022 16:03:17 +0800
Subject: [PATCH] c++: warn on Base* to Derived* cast in Base ctor/dtor
 [PR96765]

Casting "this" in a base class constructor to a derived class type is
undefined behaviour, but there is no warning when doing so.

Add a warning for this.

Signed-off-by: Zhao Wei Liew <zhaoweiliew@gmail.com>

	PR c++/96765

gcc/cp/ChangeLog:

	* typeck.cc (build_static_cast_1): Add a warning when casting
	  Base * to Derived * in Base constructor and destructor.

gcc/testsuite/ChangeLog:

	* g++.dg/warn/Wcast-qual3.C: New test.
---
 gcc/cp/typeck.cc                        |  8 ++++++
 gcc/testsuite/g++.dg/warn/Wcast-qual3.C | 33 +++++++++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/warn/Wcast-qual3.C
  

Patch

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index f796337f73c..bbc40b25547 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -8080,6 +8080,14 @@  build_static_cast_1 (location_t loc, tree type, tree expr, bool c_cast_p,
     {
       tree base;
 
+      if ((DECL_CONSTRUCTOR_P (current_function_decl)
+            || DECL_DESTRUCTOR_P (current_function_decl))
+           && TREE_CODE (expr) == NOP_EXPR
+           && is_this_parameter (TREE_OPERAND (expr, 0)))
+        warning_at(loc, OPT_Wcast_qual,
+                   "invalid %<static_cast%> from type %qT to type %qT before the latter is constructed",
+                   intype, type);
+
       if (processing_template_decl)
 	return expr;
 
diff --git a/gcc/testsuite/g++.dg/warn/Wcast-qual3.C b/gcc/testsuite/g++.dg/warn/Wcast-qual3.C
new file mode 100644
index 00000000000..8c44a23bd68
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wcast-qual3.C
@@ -0,0 +1,33 @@ 
+// PR c++/96765
+// { dg-options "-Wcast-qual" }
+
+struct Derived;
+struct Base {
+  Derived *x;
+  Derived *y;
+  Base();
+  ~Base();
+};
+
+struct Derived : Base {};
+
+Base::Base()
+    : x(static_cast<Derived *>(this)), // { dg-warning "invalid 'static_cast'" }
+      y((Derived *)this) // { dg-warning "invalid 'static_cast'" }
+{ 
+  static_cast<Derived *>(this); // { dg-warning "invalid 'static_cast'" }
+  (Derived *)this; // { dg-warning "invalid 'static_cast'" }
+}
+
+Base::~Base() {
+  static_cast<Derived *>(this); // { dg-warning "invalid 'static_cast'" }
+  (Derived *)this; // { dg-warning "invalid 'static_cast'" }
+}
+
+struct Other {
+  Other() {
+    Base b;
+    static_cast<Derived *>(&b);
+    (Derived *)(&b);
+  }
+};