On 12/16/19 3:55 PM, Jason Merrill wrote:
On 12/14/19 4:25 PM, Marek Polacek wrote:
On Fri, Dec 13, 2019 at 05:56:57PM -0500, Jason Merrill wrote:
On 12/13/19 3:20 PM, Marek Polacek wrote:
+  /* Given dynamic_cast<T>(v),
+
+     [expr.dynamic.cast] If C is the class type to which T points or refers,
+     the runtime check logically executes as follows:
+
+     If, in the most derived object pointed (referred) to by v, v points +     (refers) to a public base class subobject of a C object, and if only +     one object of type C is derived from the subobject pointed (referred)
+     to by v the result points (refers) to that C object.
+
+     In this case, HINT >= 0.  */
+  if (hint >= 0)

Won't this code work for hint == -3 as well?

Yes, it does.  In fact, none of the tests was testing the hint == -3 case, so
I've fixed the code up and added constexpr-dynamic15.C to test it.

+    {
+      /* Look for a component with type TYPE.  */
+      obj = get_component_with_type (obj, type);

You don't seem to use mdtype at all in this case.  Shouldn't
get_component_with_type stop at mdtype if it hasn't found type yet?

It was used for diagnostics but not in get_component_with_type.  It makes
sense to stop at MDTYPE; I've adjusted the code to do so.  E.g., if
we have OBJ in the form of g.D.2121.D.2122.D.2123.D.2124, usually the
component with the most derived type is "g", but in a 'tor, it can be
a different component too.

+      /* If not found or not accessible, give an error.  */
+      if (obj == NULL_TREE || obj == error_mark_node)
+    {
+      if (reference_p)
+        {
+          if (!ctx->quiet)
+        {
+          error_at (loc, "reference %<dynamic_cast%> failed");
+          if (obj == NULL_TREE)
+            inform (loc, "dynamic type %qT of its operand does not "
+                "have an unambiguous public base class %qT",
+                mdtype, type);
+          else
+            inform (loc, "static type %qT of its operand is a "
+                "non-public base class of dynamic type %qT",
+                objtype, type);
+
+        }
+          *non_constant_p = true;
+        }
+      return integer_zero_node;
+    }
+      else
+    /* The result points to the TYPE object.  */
+    return cp_build_addr_expr (obj, complain);
+    }
+  /* Otherwise, if v points (refers) to a public base class subobject of the +     most derived object, and the type of the most derived object has a base +     class, of type C, that is unambiguous and public, the result points
+     (refers) to the C subobject of the most derived object.
+
+     But it can also be an invalid case.  */

And I think we need to fall through to this code if the hint turns out to be wrong, i.e. V is a public base of C, but v is not that subobject, but rather
a sibling base of C, like

True.  HINT is really just an optimization hint, nothing more.  I've adjusted the code to fall through to the normal processing if the HINT >= 0 or -3 case
doesn't succeed.

struct A { virtual void f(); };
struct B1: A { };
struct B2: A { };
struct C: B1, B2 { };
int main()
{
   C c;
   A* ap = (B1*)c;
   constexpr auto p = dynamic_cast<B2*>(ap); // should succeed
}

Whew, there's always One More Case. :/  New constexpr-dynamic16.c covers it.

--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
@@ -0,0 +1,34 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a constructor.
+
+struct V {
+  virtual void f();
+};
+
+struct A : V { };
+
+struct B : V {
+  constexpr B(V*, A*);
+};
+
+struct D : A, B {
+  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  // well-defined: v of type V*, V base of B results in B*
+  B* b = dynamic_cast<B*>(v);
+  if (b != nullptr)
+    __builtin_abort ();
+
+  B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" } +// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target *-*-* } .-1 }
+
+  // FIXME: UB in constexpr should be detected.
+  dynamic_cast<B*>(a);          // undefined behavior, a has type A*, A not a base of B

What undefined behavior?  Seems to me the dynamic_cast should just fail and
return a null pointer.

This is <http://eel.is/c++draft/class.cdtor#6.sentence-3>:
"If the operand of the dynamic_cast refers to the object under construction
or destruction and the static type of the operand is not a pointer to or
object of the constructor or destructor's own class or one of its bases, the
dynamic_cast results in undefined behavior."

Clang++ doesn't detect this either.

Ah, interesting.  That text goes back to C++98.  I guess the reason for that was that the vtable pointer might give problematic answers in that situation.  It should be straightforward to detect this in constexpr evaluation; if mdtype is not the actual complete object, we can see if *this is also part of mdtype (success), or otherwise the complete object (undefined).

It looks like get_component_with_type (<*this>, type, mdtype) should work well 
for that check.

Well, the second argument is wrong, we'll need to find the complete object type for it.

Jason

Reply via email to