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.
Jason