royjacobson created this revision. royjacobson added reviewers: erichkeane, cor3ntin. Herald added a project: All. royjacobson requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Up to C++20, hasDefaultConstructor and !hasNonTrivialDefaultConstructor together implied hasTrivialDefaultConstructor. In C++20, a constructor might be ineligible and can set hasDefaultConstructor without setting hasNonTrivialDefaultConstructor. Fix this by querying hasTrivialDefaultConstructor instead of hasDefaultConstructor. I'd like to backport this to Clang 16. I only change isTrivialType and in a way that should only affect code that uses constrained constructors, so I think this is relatively safe to backport. Fixes https://github.com/llvm/llvm-project/issues/60697 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D143891 Files: clang/lib/AST/Type.cpp clang/test/SemaCXX/constrained-special-member-functions.cpp Index: clang/test/SemaCXX/constrained-special-member-functions.cpp =================================================================== --- clang/test/SemaCXX/constrained-special-member-functions.cpp +++ clang/test/SemaCXX/constrained-special-member-functions.cpp @@ -265,3 +265,37 @@ static_assert(__is_trivially_constructible(D), ""); } + +namespace GH60697 { + +template <class T> +struct X { + X() requires false = default; +}; +static_assert(!__is_trivial(X<int>)); + +template <class T> +struct S { + S() requires(__is_trivially_constructible(T)) = default; + + S() requires(!__is_trivially_constructible(T) && + __is_constructible(T)) {} + + T t; +}; + +struct D { + D(int i) : i(i) {} + int i; +}; +static_assert(!__is_trivially_constructible(D)); +static_assert(!__is_constructible(D)); +static_assert(!__is_trivial(D)); + +static_assert(!__is_trivially_constructible(S<D>)); +static_assert(!__is_constructible(S<D>)); + +static_assert(__is_trivial(S<int>)); +static_assert(!__is_trivial(S<D>)); + +} Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -2487,11 +2487,13 @@ return true; if (const auto *RT = CanonicalType->getAs<RecordType>()) { if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) { - // C++11 [class]p6: - // A trivial class is a class that has a default constructor, - // has no non-trivial default constructors, and is trivially - // copyable. - return ClassDecl->hasDefaultConstructor() && + // C++20 [class]p6: + // A trivial class is a class that is trivially copyable, and + // has one or more eligible default constructors such that each is + // trivial. + // FIXME: We should merge this definition of triviality into + // CXXRecordDecl::isTrivial. Currently it computes the wrong thing. + return ClassDecl->hasTrivialDefaultConstructor() && !ClassDecl->hasNonTrivialDefaultConstructor() && ClassDecl->isTriviallyCopyable(); }
Index: clang/test/SemaCXX/constrained-special-member-functions.cpp =================================================================== --- clang/test/SemaCXX/constrained-special-member-functions.cpp +++ clang/test/SemaCXX/constrained-special-member-functions.cpp @@ -265,3 +265,37 @@ static_assert(__is_trivially_constructible(D), ""); } + +namespace GH60697 { + +template <class T> +struct X { + X() requires false = default; +}; +static_assert(!__is_trivial(X<int>)); + +template <class T> +struct S { + S() requires(__is_trivially_constructible(T)) = default; + + S() requires(!__is_trivially_constructible(T) && + __is_constructible(T)) {} + + T t; +}; + +struct D { + D(int i) : i(i) {} + int i; +}; +static_assert(!__is_trivially_constructible(D)); +static_assert(!__is_constructible(D)); +static_assert(!__is_trivial(D)); + +static_assert(!__is_trivially_constructible(S<D>)); +static_assert(!__is_constructible(S<D>)); + +static_assert(__is_trivial(S<int>)); +static_assert(!__is_trivial(S<D>)); + +} Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -2487,11 +2487,13 @@ return true; if (const auto *RT = CanonicalType->getAs<RecordType>()) { if (const auto *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) { - // C++11 [class]p6: - // A trivial class is a class that has a default constructor, - // has no non-trivial default constructors, and is trivially - // copyable. - return ClassDecl->hasDefaultConstructor() && + // C++20 [class]p6: + // A trivial class is a class that is trivially copyable, and + // has one or more eligible default constructors such that each is + // trivial. + // FIXME: We should merge this definition of triviality into + // CXXRecordDecl::isTrivial. Currently it computes the wrong thing. + return ClassDecl->hasTrivialDefaultConstructor() && !ClassDecl->hasNonTrivialDefaultConstructor() && ClassDecl->isTriviallyCopyable(); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits