aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2019-12-29 16:44:41 +0000
committerMarek Polacek <mpolacek@gcc.gnu.org>2019-12-29 16:44:41 +0000
commit22edf9431e4519c409ebf41c7589cccfb8c4b625 (patch)
treec4cb09adb7774b0c2c01d580d49c46ea9d4a394e /gcc/cp
parent6ec067548fa994158819db0a62a8b5356d452c2c (diff)
downloadgcc-22edf9431e4519c409ebf41c7589cccfb8c4b625.zip
gcc-22edf9431e4519c409ebf41c7589cccfb8c4b625.tar.gz
gcc-22edf9431e4519c409ebf41c7589cccfb8c4b625.tar.bz2
PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
This patch implements <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1327r1.html>. When build_dynamic_cast realizes that a dynamic_cast needs a run-time check, it generates a call to __dynamic_cast -- see dyncast.cc in libsupc++ for its definition. The gist of my approach is to evaluate such a call at compile time. * constexpr.c (cxx_dynamic_cast_fn_p): New function. (extract_obj_from_addr_offset): New function. (get_component_with_type): New function. (cxx_eval_dynamic_cast_fn): New function. (cxx_eval_call_expression): Call cxx_eval_dynamic_cast_fn for a call to __dynamic_cast. (potential_constant_expression_1): Don't give up on cxx_dynamic_cast_fn_p. * rtti.c (build_dynamic_cast_1): When creating a call to __dynamic_cast, use the location of the original expression. * g++.dg/cpp2a/constexpr-dynamic1.C: New test. * g++.dg/cpp2a/constexpr-dynamic10.C: New test. * g++.dg/cpp2a/constexpr-dynamic11.C: New test. * g++.dg/cpp2a/constexpr-dynamic12.C: New test. * g++.dg/cpp2a/constexpr-dynamic13.C: New test. * g++.dg/cpp2a/constexpr-dynamic14.C: New test. * g++.dg/cpp2a/constexpr-dynamic15.C: New test. * g++.dg/cpp2a/constexpr-dynamic16.C: New test. * g++.dg/cpp2a/constexpr-dynamic17.C: New test. * g++.dg/cpp2a/constexpr-dynamic2.C: New test. * g++.dg/cpp2a/constexpr-dynamic3.C: New test. * g++.dg/cpp2a/constexpr-dynamic4.C: New test. * g++.dg/cpp2a/constexpr-dynamic5.C: New test. * g++.dg/cpp2a/constexpr-dynamic6.C: New test. * g++.dg/cpp2a/constexpr-dynamic7.C: New test. * g++.dg/cpp2a/constexpr-dynamic8.C: New test. * g++.dg/cpp2a/constexpr-dynamic9.C: New test. From-SVN: r279755
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/ChangeLog14
-rw-r--r--gcc/cp/constexpr.c238
-rw-r--r--gcc/cp/rtti.c1
3 files changed, 252 insertions, 1 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 334438c..37d776c 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,17 @@
+2019-12-29 Marek Polacek <polacek@redhat.com>
+
+ PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
+ * constexpr.c (cxx_dynamic_cast_fn_p): New function.
+ (extract_obj_from_addr_offset): New function.
+ (get_component_with_type): New function.
+ (cxx_eval_dynamic_cast_fn): New function.
+ (cxx_eval_call_expression): Call cxx_eval_dynamic_cast_fn for a call
+ to __dynamic_cast.
+ (potential_constant_expression_1): Don't give up on
+ cxx_dynamic_cast_fn_p.
+ * rtti.c (build_dynamic_cast_1): When creating a call to
+ __dynamic_cast, use the location of the original expression.
+
2019-12-26 Jakub Jelinek <jakub@redhat.com>
PR c++/92438
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index b95da0f..31ad6d5 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1699,6 +1699,238 @@ is_std_allocator_allocate (tree fndecl)
return decl_in_std_namespace_p (decl);
}
+/* Return true if FNDECL is __dynamic_cast. */
+
+static inline bool
+cxx_dynamic_cast_fn_p (tree fndecl)
+{
+ return (cxx_dialect >= cxx2a
+ && id_equal (DECL_NAME (fndecl), "__dynamic_cast")
+ && CP_DECL_CONTEXT (fndecl) == global_namespace);
+}
+
+/* Often, we have an expression in the form of address + offset, e.g.
+ "&_ZTV1A + 16". Extract the object from it, i.e. "_ZTV1A". */
+
+static tree
+extract_obj_from_addr_offset (tree expr)
+{
+ if (TREE_CODE (expr) == POINTER_PLUS_EXPR)
+ expr = TREE_OPERAND (expr, 0);
+ STRIP_NOPS (expr);
+ if (TREE_CODE (expr) == ADDR_EXPR)
+ expr = TREE_OPERAND (expr, 0);
+ return expr;
+}
+
+/* Given a PATH like
+
+ g.D.2181.D.2154.D.2102.D.2093
+
+ find a component with type TYPE. Return NULL_TREE if not found, and
+ error_mark_node if the component is not accessible. If STOP is non-null,
+ this function will return NULL_TREE if STOP is found before TYPE. */
+
+static tree
+get_component_with_type (tree path, tree type, tree stop)
+{
+ while (true)
+ {
+ if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), type))
+ /* Found it. */
+ return path;
+ else if (stop
+ && (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path),
+ stop)))
+ return NULL_TREE;
+ else if (TREE_CODE (path) == COMPONENT_REF
+ && DECL_FIELD_IS_BASE (TREE_OPERAND (path, 1)))
+ {
+ /* We need to check that the component we're accessing is in fact
+ accessible. */
+ if (TREE_PRIVATE (TREE_OPERAND (path, 1))
+ || TREE_PROTECTED (TREE_OPERAND (path, 1)))
+ return error_mark_node;
+ path = TREE_OPERAND (path, 0);
+ }
+ else
+ return NULL_TREE;
+ }
+}
+
+/* Evaluate a call to __dynamic_cast (permitted by P1327R1).
+
+ The declaration of __dynamic_cast is:
+
+ void* __dynamic_cast (const void* __src_ptr,
+ const __class_type_info* __src_type,
+ const __class_type_info* __dst_type,
+ ptrdiff_t __src2dst);
+
+ where src2dst has the following possible values
+
+ >-1: src_type is a unique public non-virtual base of dst_type
+ dst_ptr + src2dst == src_ptr
+ -1: unspecified relationship
+ -2: src_type is not a public base of dst_type
+ -3: src_type is a multiple public non-virtual base of dst_type
+
+ Since literal types can't have virtual bases, we only expect hint >=0,
+ -2, or -3. */
+
+static tree
+cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
+ bool *non_constant_p, bool *overflow_p)
+{
+ /* T will be something like
+ __dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8)
+ dismantle it. */
+ gcc_assert (call_expr_nargs (call) == 4);
+ tsubst_flags_t complain = ctx->quiet ? tf_none : tf_warning_or_error;
+ tree obj = CALL_EXPR_ARG (call, 0);
+ tree type = CALL_EXPR_ARG (call, 2);
+ HOST_WIDE_INT hint = int_cst_value (CALL_EXPR_ARG (call, 3));
+ location_t loc = cp_expr_loc_or_input_loc (call);
+
+ /* Get the target type of the dynamic_cast. */
+ gcc_assert (TREE_CODE (type) == ADDR_EXPR);
+ type = TREE_OPERAND (type, 0);
+ type = TREE_TYPE (DECL_NAME (type));
+
+ /* TYPE can only be either T* or T&. We can't know which of these it
+ is by looking at TYPE, but OBJ will be "(T*) x" in the first case,
+ and something like "(T*)(T&)(T*) x" in the second case. */
+ bool reference_p = false;
+ while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR)
+ {
+ reference_p |= TYPE_REF_P (TREE_TYPE (obj));
+ obj = TREE_OPERAND (obj, 0);
+ }
+
+ /* Evaluate the object so that we know its dynamic type. */
+ obj = cxx_eval_constant_expression (ctx, obj, /*lval*/false, non_constant_p,
+ overflow_p);
+ if (*non_constant_p)
+ return call;
+
+ /* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
+ but when HINT is > 0, it can also be something like
+ &d.D.2102 + 18446744073709551608, which includes the BINFO_OFFSET. */
+ obj = extract_obj_from_addr_offset (obj);
+ const tree objtype = TREE_TYPE (obj);
+ /* If OBJ doesn't refer to a base field, we're done. */
+ if (tree t = (TREE_CODE (obj) == COMPONENT_REF
+ ? TREE_OPERAND (obj, 1) : obj))
+ if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t))
+ return integer_zero_node;
+
+ /* [class.cdtor] When a dynamic_cast is used in a constructor ...
+ or in a destructor ... if the operand of the dynamic_cast refers
+ to the object under construction or destruction, this object is
+ considered to be a most derived object that has the type of the
+ constructor or destructor's class. */
+ tree vtable = build_vfield_ref (obj, TREE_TYPE (obj));
+ vtable = cxx_eval_constant_expression (ctx, vtable, /*lval*/false,
+ non_constant_p, overflow_p);
+ if (*non_constant_p)
+ return call;
+ /* VTABLE will be &_ZTV1A + 16 or similar, get _ZTV1A. */
+ vtable = extract_obj_from_addr_offset (vtable);
+ const tree mdtype = DECL_CONTEXT (vtable);
+
+ /* 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 or -3. */
+ if (hint >= 0 || hint == -3)
+ {
+ /* Look for a component with type TYPE. */
+ tree t = get_component_with_type (obj, type, mdtype);
+ /* If not accessible, give an error. */
+ if (t == error_mark_node)
+ {
+ if (reference_p)
+ {
+ if (!ctx->quiet)
+ {
+ error_at (loc, "reference %<dynamic_cast%> failed");
+ 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 if (t)
+ /* The result points to the TYPE object. */
+ return cp_build_addr_expr (t, complain);
+ /* Else, TYPE was not found, because the HINT turned out to be wrong.
+ Fall through to the normal processing. */
+ }
+
+ /* 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. */
+
+ /* Get the most derived object. */
+ obj = get_component_with_type (obj, mdtype, NULL_TREE);
+ if (obj == error_mark_node)
+ {
+ if (reference_p)
+ {
+ if (!ctx->quiet)
+ {
+ error_at (loc, "reference %<dynamic_cast%> failed");
+ inform (loc, "static type %qT of its operand is a non-public"
+ " base class of dynamic type %qT", objtype, mdtype);
+ }
+ *non_constant_p = true;
+ }
+ return integer_zero_node;
+ }
+ else
+ gcc_assert (obj);
+
+ /* Check that the type of the most derived object has a base class
+ of type TYPE that is unambiguous and public. */
+ base_kind b_kind;
+ tree binfo = lookup_base (mdtype, type, ba_check, &b_kind, tf_none);
+ if (!binfo || binfo == error_mark_node)
+ {
+ if (reference_p)
+ {
+ if (!ctx->quiet)
+ {
+ error_at (loc, "reference %<dynamic_cast%> failed");
+ if (b_kind == bk_ambig)
+ inform (loc, "%qT is an ambiguous base class of dynamic "
+ "type %qT of its operand", type, mdtype);
+ else
+ inform (loc, "dynamic type %qT of its operand does not "
+ "have an unambiguous public base class %qT",
+ mdtype, type);
+ }
+ *non_constant_p = true;
+ }
+ return integer_zero_node;
+ }
+ /* If so, return the TYPE subobject of the most derived object. */
+ obj = convert_to_base_statically (obj, binfo);
+ return cp_build_addr_expr (obj, complain);
+}
+
/* Subroutine of cxx_eval_constant_expression.
Evaluate the call expression tree T in the context of OLD_CALL expression
evaluation. */
@@ -1864,6 +2096,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
gcc_assert (arg1);
return arg1;
}
+ else if (cxx_dynamic_cast_fn_p (fun))
+ return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
+
if (!ctx->quiet)
{
if (!lambda_static_thunk_p (fun))
@@ -6740,7 +6975,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
&& (!cxx_placement_new_fn (fun)
|| TREE_CODE (t) != CALL_EXPR
|| current_function_decl == NULL_TREE
- || !is_std_construct_at (current_function_decl)))
+ || !is_std_construct_at (current_function_decl))
+ && !cxx_dynamic_cast_fn_p (fun))
{
if (flags & tf_error)
{
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index 1b6b87b..9a242dc 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
dynamic_cast_node = dcast_fn;
}
result = build_cxx_call (dcast_fn, 4, elems, complain);
+ SET_EXPR_LOCATION (result, loc);
if (tc == REFERENCE_TYPE)
{