]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: implement DR1363 and DR1496 for __is_trivial [PR85723]
authorMarek Polacek <polacek@redhat.com>
Tue, 18 Jun 2024 20:49:24 +0000 (16:49 -0400)
committerMarek Polacek <polacek@redhat.com>
Thu, 18 Jul 2024 19:06:15 +0000 (15:06 -0400)
is_trivial was introduced in
<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2230.html>
which split POD into is_trivial and is_standard_layout.

Later came CWG 1363.  Since

  struct A {
    A() = default;
    A(int = 42) {}
  };

cannot be default-initialized, it should not be trivial, so the definition
of what is a trivial class changed.

Similarly, CWG 1496 concluded that

  struct B {
    B() = delete;
  }:

should not be trivial either.

P0848 adjusted the definition further to say "eligible".  That means
that

  template<typename T>
  struct C {
    C() requires false = default;
  };

should not be trivial, either, since C::C() is not eligible.

Bug 85723 reports that we implement none of the CWGs.

I chose to fix this by using type_has_non_deleted_trivial_default_ctor
which uses locate_ctor which uses build_new_method_call, which would
be used by default-initialization as well.  With that, all __is_trivial
problems I could find in the Bugzilla are fixed, except for PR96288,
which may need changes to trivially-copyable, so I'm not messing with
that now.

I hope this has no ABI implications.  There's effort undergoing to
remove "trivial class" from the core language as it's not really
meaningful.  So the impact of this change should be pretty low except
to fix a few libstdc++ problems.

PR c++/108769
PR c++/58074
PR c++/115522
PR c++/85723

gcc/cp/ChangeLog:

* class.cc (type_has_non_deleted_trivial_default_ctor): Fix formatting.
* tree.cc (trivial_type_p): Instead of TYPE_HAS_TRIVIAL_DFLT, use
type_has_non_deleted_trivial_default_ctor.

gcc/testsuite/ChangeLog:

* g++.dg/warn/Wclass-memaccess.C: Add dg-warning.
* g++.dg/ext/is_trivial1.C: New test.
* g++.dg/ext/is_trivial2.C: New test.
* g++.dg/ext/is_trivial3.C: New test.
* g++.dg/ext/is_trivial4.C: New test.
* g++.dg/ext/is_trivial5.C: New test.
* g++.dg/ext/is_trivial6.C: New test.

gcc/cp/class.cc
gcc/cp/tree.cc
gcc/testsuite/g++.dg/ext/is_trivial1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/is_trivial2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/is_trivial3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/is_trivial4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/is_trivial5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/is_trivial6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wclass-memaccess.C

index 0ce361eb88e5830f053c07f5024d0afac7836b5e..718601756ddc154d643644aa19d380ef2803a52e 100644 (file)
@@ -5918,7 +5918,8 @@ type_has_virtual_destructor (tree type)
 /* True iff class TYPE has a non-deleted trivial default
    constructor.  */
 
-bool type_has_non_deleted_trivial_default_ctor (tree type)
+bool
+type_has_non_deleted_trivial_default_ctor (tree type)
 {
   return TYPE_HAS_TRIVIAL_DFLT (type) && locate_ctor (type);
 }
index dfd4a3a948b1baa4a6065ee29bfdf06c8fd0327e..0e32d908b060892cd0c1b4d3c109060de8ee59dd 100644 (file)
@@ -4637,7 +4637,9 @@ trivial_type_p (const_tree t)
   t = strip_array_types (CONST_CAST_TREE (t));
 
   if (CLASS_TYPE_P (t))
-    return (TYPE_HAS_TRIVIAL_DFLT (t)
+    /* A trivial class is a class that is trivially copyable and has one or
+       more eligible default constructors, all of which are trivial.  */
+    return (type_has_non_deleted_trivial_default_ctor (CONST_CAST_TREE (t))
            && trivially_copyable_p (t));
   else
     return scalarish_type_p (t);
diff --git a/gcc/testsuite/g++.dg/ext/is_trivial1.C b/gcc/testsuite/g++.dg/ext/is_trivial1.C
new file mode 100644 (file)
index 0000000..60ce48e
--- /dev/null
@@ -0,0 +1,14 @@
+// PR c++/108769
+// { dg-do compile { target c++20 } }
+
+template <class T>
+struct S {
+    S() requires false = default;
+};
+static_assert(!__is_trivial(S<int>));
+
+template <class T>
+struct R {
+    R() requires true = default;
+};
+static_assert(__is_trivial(R<int>));
diff --git a/gcc/testsuite/g++.dg/ext/is_trivial2.C b/gcc/testsuite/g++.dg/ext/is_trivial2.C
new file mode 100644 (file)
index 0000000..e7ecc74
--- /dev/null
@@ -0,0 +1,20 @@
+// PR c++/58074
+// { dg-do compile { target c++11 } }
+
+struct Trivial
+{
+  Trivial() = delete;
+};
+
+struct NonTrivial
+{
+  NonTrivial() = default;
+  NonTrivial(NonTrivial&) = default;
+  NonTrivial& operator=(NonTrivial&) = default;
+};
+
+// As it happens, 58074 was originally asking for the opposite result
+// of __is_trivial than we're checking here, so what was non-trivial
+// was supposed to be trivial and vice versa.  But here we are.
+static_assert(!__is_trivial(Trivial), "Ouch");
+static_assert(__is_trivial(NonTrivial), "Ouch");
diff --git a/gcc/testsuite/g++.dg/ext/is_trivial3.C b/gcc/testsuite/g++.dg/ext/is_trivial3.C
new file mode 100644 (file)
index 0000000..9704a94
--- /dev/null
@@ -0,0 +1,15 @@
+// PR c++/115522
+// { dg-do compile { target c++11 } }
+
+// Not default constructible.
+struct S {
+  const int i;
+};
+
+static_assert(!__is_trivial(S), "");
+
+struct R {
+  int &r;
+};
+
+static_assert(!__is_trivial(R), "");
diff --git a/gcc/testsuite/g++.dg/ext/is_trivial4.C b/gcc/testsuite/g++.dg/ext/is_trivial4.C
new file mode 100644 (file)
index 0000000..c26e784
--- /dev/null
@@ -0,0 +1,10 @@
+// CWG 1363
+// PR c++/85723
+// { dg-do compile { target c++11 } }
+
+struct A {
+    A() = default;
+    A(int i = 0) { }
+};
+
+static_assert(!__is_trivial(A), "");
diff --git a/gcc/testsuite/g++.dg/ext/is_trivial5.C b/gcc/testsuite/g++.dg/ext/is_trivial5.C
new file mode 100644 (file)
index 0000000..5c89e1d
--- /dev/null
@@ -0,0 +1,8 @@
+// CWG 1496
+// PR c++/85723
+// { dg-do compile { target c++11 } }
+
+struct NonTrivial {
+  NonTrivial() = delete;
+};
+static_assert(!__is_trivial (NonTrivial), "NonTrivial is trivial");
diff --git a/gcc/testsuite/g++.dg/ext/is_trivial6.C b/gcc/testsuite/g++.dg/ext/is_trivial6.C
new file mode 100644 (file)
index 0000000..a3a688c
--- /dev/null
@@ -0,0 +1,49 @@
+// PR c++/85723
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+struct A {
+  A() = delete;
+  A() requires(sizeof(T) == 1) = default;
+  A() requires(sizeof(T) != 1) = delete;
+};
+static_assert(!__is_trivial(A<int>));
+static_assert(__is_trivial(A<char>));
+
+template<typename T>
+struct B {
+  B() = default;
+  B() requires(sizeof(T) == 1) = default;
+  B() requires(sizeof(T) != 1) = delete;
+};
+static_assert(__is_trivial(B<int>));
+static_assert(__is_trivial(B<char>));
+
+template<typename T>
+struct C {
+  C() = default;
+  C() requires(sizeof(T) == 1) = delete;
+  C() requires(sizeof(T) != 1) = default;
+};
+static_assert(__is_trivial(C<int>));
+static_assert(__is_trivial(C<char>));
+
+template<typename T>
+struct D {
+  D() = default;
+  D(int = 42) {}
+  D() requires(sizeof(T) == 1) = delete;
+  D() requires(sizeof(T) != 1) = default;
+};
+static_assert(!__is_trivial(D<int>));
+static_assert(!__is_trivial(D<char>));
+
+
+template<typename T>
+struct E {
+  E() = delete;
+  E() requires(sizeof(T) == 1) = default;
+  E() requires(sizeof(T) != 1) = delete;
+};
+static_assert(!__is_trivial(E<int>));
+static_assert(__is_trivial(E<char>));
index 87aaa79ceca07cd98c55c0ce44555bbc49291eb4..636a6e6e1b86f69d1257a956ddeccf2b9eb3c049 100644 (file)
@@ -1254,6 +1254,7 @@ void test (HasConstData *p, const HasConstData &x,
 
   // Reallocating is not diagnosed except in C++ 98 due to a bug.
   T (q = realloc, (p, 1));          // { dg-warning "moving an object of non-trivially copyable type .struct HasConstData.; use .new. and .delete. instead" "c++98" { target { c++98_only } } }
+// { dg-warning "moving an object of non-trivial type" "" { target c++11 } .-1 }
   T (q = realloc, (p, n));          // { dg-warning "realloc" "c++98" { target { c++98_only } } }
   T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++98" { target { c++98_only } } }
 }
@@ -1333,6 +1334,7 @@ void test (HasReference *p, const HasReference &x,
   // in C++ 98 because of a bug, but it seems like it should be
   // diagnosed in all modes.
   T (q = realloc, (p, 1));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
+// { dg-warning "moving an object of non-trivial type" "" { target c++11 } .-1 }
   T (q = realloc, (p, n));          // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
   T (q = realloc, (p, sizeof *p));  // { dg-warning "realloc" "c++ 98" { target { c++98_only } } }
 }