case CPTK_IS_CONVERTIBLE:
inform (loc, " %qT is not convertible from %qE", t2, t1);
break;
+ case CPTK_IS_DESTRUCTIBLE:
+ inform (loc, " %qT is not destructible", t1);
+ break;
case CPTK_IS_EMPTY:
inform (loc, " %qT is not an empty class", t1);
break;
case CPTK_IS_NOTHROW_CONVERTIBLE:
inform (loc, " %qT is not nothrow convertible from %qE", t2, t1);
break;
+ case CPTK_IS_NOTHROW_DESTRUCTIBLE:
+ inform (loc, " %qT is not nothrow destructible", t1);
+ break;
case CPTK_IS_NOTHROW_INVOCABLE:
if (!t2)
inform (loc, " %qT is not nothrow invocable", t1);
case CPTK_IS_TRIVIALLY_COPYABLE:
inform (loc, " %qT is not trivially copyable", t1);
break;
+ case CPTK_IS_TRIVIALLY_DESTRUCTIBLE:
+ inform (loc, " %qT is not trivially destructible", t1);
+ break;
case CPTK_IS_UNBOUNDED_ARRAY:
inform (loc, " %qT is not an unbounded array", t1);
break;
DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
+DEFTRAIT_EXPR (IS_DESTRUCTIBLE, "__is_destructible", 1)
DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2)
DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1)
DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
+DEFTRAIT_EXPR (IS_NOTHROW_DESTRUCTIBLE, "__is_nothrow_destructible", 1)
DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1)
DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_TRIVIALLY_DESTRUCTIBLE, "__is_trivially_destructible", -1)
DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
DEFTRAIT_EXPR (IS_VIRTUAL_BASE_OF, "__builtin_is_virtual_base_of", 2)
return expr;
}
+/* Return declval<T>().~T() treated as an unevaluated operand. */
+
+static tree
+destructible_expr (tree to)
+{
+ cp_unevaluated cp_uneval_guard;
+ int flags = LOOKUP_NORMAL|LOOKUP_DESTRUCTOR;
+ to = build_trait_object (to);
+ tree r = build_delete (input_location, TREE_TYPE (to), to,
+ sfk_complete_destructor, flags, 0, tf_none);
+ return r;
+}
+
/* Returns a tree iff TO is assignable (if CODE is MODIFY_EXPR) or
constructible (otherwise) from FROM, which is a single type for
assignment or a list of types for construction. */
tree expr;
if (code == MODIFY_EXPR)
expr = assignable_expr (to, from);
+ else if (code == BIT_NOT_EXPR)
+ expr = destructible_expr (to);
else if (trivial && TREE_VEC_LENGTH (from) > 1
&& cxx_dialect < cxx20)
return error_mark_node; // only 0- and 1-argument ctors can be trivial
case CPTK_HAS_TRIVIAL_DESTRUCTOR:
type1 = strip_array_types (type1);
+ if (CLASS_TYPE_P (type1) && type_build_dtor_call (type1))
+ {
+ deferring_access_check_sentinel dacs (dk_no_check);
+ tree fn = get_dtor (type1, tf_none);
+ if (!fn && !seen_error ())
+ warning (0, "checking %qs for type %qT with a destructor that "
+ "cannot be called", "__has_trivial_destructor", type1);
+ }
return (trivial_type_p (type1) || type_code1 == REFERENCE_TYPE
|| (CLASS_TYPE_P (type1)
&& TYPE_HAS_TRIVIAL_DESTRUCTOR (type1)));
case CPTK_IS_CONVERTIBLE:
return is_convertible (type1, type2);
+ case CPTK_IS_DESTRUCTIBLE:
+ return is_xible (BIT_NOT_EXPR, type1, NULL_TREE);
+
case CPTK_IS_EMPTY:
return NON_UNION_CLASS_TYPE_P (type1) && CLASSTYPE_EMPTY_P (type1);
case CPTK_IS_NOTHROW_CONVERTIBLE:
return is_nothrow_convertible (type1, type2);
+ case CPTK_IS_NOTHROW_DESTRUCTIBLE:
+ return is_nothrow_xible (BIT_NOT_EXPR, type1, NULL_TREE);
+
case CPTK_IS_NOTHROW_INVOCABLE:
return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none);
case CPTK_IS_TRIVIALLY_COPYABLE:
return trivially_copyable_p (type1);
+ case CPTK_IS_TRIVIALLY_DESTRUCTIBLE:
+ return is_trivially_xible (BIT_NOT_EXPR, type1, NULL_TREE);
+
case CPTK_IS_UNBOUNDED_ARRAY:
return array_of_unknown_bound_p (type1);
case CPTK_HAS_NOTHROW_COPY:
case CPTK_HAS_TRIVIAL_COPY:
case CPTK_HAS_TRIVIAL_DESTRUCTOR:
+ case CPTK_IS_DESTRUCTIBLE:
+ case CPTK_IS_NOTHROW_DESTRUCTIBLE:
+ case CPTK_IS_TRIVIALLY_DESTRUCTIBLE:
if (!check_trait_type (type1))
return error_mark_node;
break;
--- /dev/null
+// PR c++/107600
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert ((X), #X)
+
+namespace N1 {
+ struct A { ~A() = delete; };
+
+ SA (!__is_destructible (A));
+ SA (!__is_nothrow_destructible (A));
+ SA (!__is_trivially_destructible (A));
+
+ bool b = __has_trivial_destructor (A); // { dg-message "has_trivial_destructor" }
+}
+
+namespace N2 {
+ struct A { protected: ~A() = default; };
+
+ SA (!__is_destructible (A));
+ SA (!__is_nothrow_destructible (A));
+ SA (!__is_trivially_destructible (A));
+ SA (__has_trivial_destructor (A));
+}
+
+namespace N3 {
+ struct A { ~A(); };
+
+ SA (__is_destructible (A));
+ SA (__is_nothrow_destructible (A));
+ SA (!__is_trivially_destructible (A));
+ SA (!__has_trivial_destructor (A));
+}
+
+namespace N4 {
+ struct A { ~A() noexcept (false); };
+
+ SA (__is_destructible (A));
+ SA (!__is_nothrow_destructible (A));
+ SA (!__is_trivially_destructible (A));
+ SA (!__has_trivial_destructor (A));
+}
+
+namespace N5 {
+ struct A { ~A() = default; };
+
+ SA (__is_destructible (A));
+ SA (__is_nothrow_destructible (A));
+ SA (__is_trivially_destructible (A));
+ SA (__has_trivial_destructor (A));
+}
+
+namespace N6 {
+ struct A { };
+
+ SA (__is_destructible (A));
+ SA (__is_nothrow_destructible (A));
+ SA (__is_trivially_destructible (A));
+ SA (__has_trivial_destructor (A));
+}
+