[Clang] Implement the core language parts of P2786 - Trivial relocation (#127636)
This adds - The parsing of `trivially_relocatable_if_eligible`, `replaceable_if_eligible` keywords - `__builtin_trivially_relocate`, implemented in terms of memmove. In the future this should - Add the appropriate start/end lifetime markers that llvm does not have (`start_lifetime_as`) - Add support for ptrauth when that's upstreamed - the `__builtin_is_cpp_trivially_relocatable` and `__builtin_is_replaceable` traits Fixes #127609
This commit is contained in:
parent
3b4f9c5442
commit
300d4026f7
@ -1680,6 +1680,7 @@ Static assert with user-generated message __cpp_static_assert >= 202306L C
|
||||
Pack Indexing __cpp_pack_indexing C++26 C++03
|
||||
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
|
||||
Variadic Friends __cpp_variadic_friend C++26 C++03
|
||||
Trivial Relocatability __cpp_trivial_relocatability C++26 C++03
|
||||
--------------------------------------------- -------------------------------- ------------- -------------
|
||||
Designated initializers (N494) C99 C89
|
||||
Array & element qualification (N2607) C23 C89
|
||||
@ -1861,8 +1862,15 @@ The following type trait primitives are supported by Clang. Those traits marked
|
||||
* ``__is_trivially_relocatable`` (Clang): Returns true if moving an object
|
||||
of the given type, and then destroying the source object, is known to be
|
||||
functionally equivalent to copying the underlying bytes and then dropping the
|
||||
source object on the floor. This is true of trivial types and types which
|
||||
source object on the floor. This is true of trivial types,
|
||||
C++26 relocatable types, and types which
|
||||
were made trivially relocatable via the ``clang::trivial_abi`` attribute.
|
||||
* ``__builtin_is_cpp_trivially_relocatable`` (C++): Returns true if an object
|
||||
is trivially relocatable, as defined by the C++26 standard [meta.unary.prop].
|
||||
Note that when relocating the caller code should ensure that if the object is polymorphic,
|
||||
the dynamic type is of the most derived type. Padding bytes should not be copied.
|
||||
* ``__builtin_is_replaceable`` (C++): Returns true if an object
|
||||
is replaceable, as defined by the C++26 standard [meta.unary.prop].
|
||||
* ``__is_trivially_equality_comparable`` (Clang): Returns true if comparing two
|
||||
objects of the provided type is known to be equivalent to comparing their
|
||||
object representations. Note that types containing padding bytes are never
|
||||
@ -3722,6 +3730,21 @@ Query for this feature with ``__has_builtin(__builtin_operator_new)`` or
|
||||
replaceable global (de)allocation functions, but do support calling at least
|
||||
``::operator new(size_t)`` and ``::operator delete(void*)``.
|
||||
|
||||
|
||||
``__builtin_trivially_relocate``
|
||||
-----------------------------------
|
||||
|
||||
**Syntax**:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
T* __builtin_trivially_relocate(T* dest, T* src, size_t count)
|
||||
|
||||
Trivially relocates ``count`` objects of relocatable, complete type ``T``
|
||||
from ``src`` to ``dest`` and returns ``dest``.
|
||||
This builtin is used to implement ``std::trivially_relocate``.
|
||||
|
||||
|
||||
``__builtin_preserve_access_index``
|
||||
-----------------------------------
|
||||
|
||||
|
@ -106,6 +106,8 @@ C++2c Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Implemented `P1061R10 Structured Bindings can introduce a Pack <https://wg21.link/P1061R10>`_.
|
||||
- Implemented `P2786R13 Trivial Relocatability <https://wg21.link/P2786R13>`_.
|
||||
|
||||
|
||||
- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
|
||||
|
||||
|
@ -617,6 +617,20 @@ private:
|
||||
using ParameterIndexTable = llvm::DenseMap<const VarDecl *, unsigned>;
|
||||
ParameterIndexTable ParamIndices;
|
||||
|
||||
public:
|
||||
struct CXXRecordDeclRelocationInfo {
|
||||
unsigned IsRelocatable;
|
||||
unsigned IsReplaceable;
|
||||
};
|
||||
std::optional<CXXRecordDeclRelocationInfo>
|
||||
getRelocationInfoForCXXRecord(const CXXRecordDecl *) const;
|
||||
void setRelocationInfoForCXXRecord(const CXXRecordDecl *,
|
||||
CXXRecordDeclRelocationInfo);
|
||||
|
||||
private:
|
||||
llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
|
||||
RelocatableClasses;
|
||||
|
||||
ImportDecl *FirstLocalImport = nullptr;
|
||||
ImportDecl *LastLocalImport = nullptr;
|
||||
|
||||
|
@ -1550,6 +1550,9 @@ public:
|
||||
/// Returns the destructor decl for this class.
|
||||
CXXDestructorDecl *getDestructor() const;
|
||||
|
||||
/// Returns the destructor decl for this class.
|
||||
bool hasDeletedDestructor() const;
|
||||
|
||||
/// Returns true if the class destructor, or any implicitly invoked
|
||||
/// destructors are marked noreturn.
|
||||
bool isAnyDestructorNoReturn() const { return data().IsAnyDestructorNoReturn; }
|
||||
|
@ -1133,9 +1133,6 @@ public:
|
||||
/// Return true if this is a trivially copyable type
|
||||
bool isTriviallyCopyConstructibleType(const ASTContext &Context) const;
|
||||
|
||||
/// Return true if this is a trivially relocatable type.
|
||||
bool isTriviallyRelocatableType(const ASTContext &Context) const;
|
||||
|
||||
/// Returns true if it is a class and it might be dynamic.
|
||||
bool mayBeDynamicClass() const;
|
||||
|
||||
|
@ -1819,6 +1819,22 @@ def Final : InheritableAttr {
|
||||
let Documentation = [InternalOnly];
|
||||
}
|
||||
|
||||
def TriviallyRelocatable : InheritableAttr {
|
||||
let Spellings = [CustomKeyword<"trivially_relocatable_if_eligible">];
|
||||
let SemaHandler = 0;
|
||||
// Omitted from docs, since this is language syntax, not an attribute, as far
|
||||
// as users are concerned.
|
||||
let Documentation = [InternalOnly];
|
||||
}
|
||||
|
||||
def Replaceable : InheritableAttr {
|
||||
let Spellings = [CustomKeyword<"replaceable_if_eligible">];
|
||||
let SemaHandler = 0;
|
||||
// Omitted from docs, since this is language syntax, not an attribute, as far
|
||||
// as users are concerned.
|
||||
let Documentation = [InternalOnly];
|
||||
}
|
||||
|
||||
def MinSize : InheritableAttr {
|
||||
let Spellings = [Clang<"minsize">];
|
||||
let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>;
|
||||
|
@ -2853,6 +2853,12 @@ def MemMove : LibBuiltin<"string.h"> {
|
||||
let AddBuiltinPrefixedAlias = 1;
|
||||
}
|
||||
|
||||
def BuiltinTriviallyRelocate : Builtin {
|
||||
let Spellings = ["__builtin_trivially_relocate"];
|
||||
let Attributes = [FunctionWithBuiltinPrefix, CustomTypeChecking, NoThrow];
|
||||
let Prototype = "void*(void*, void*, size_t)";
|
||||
}
|
||||
|
||||
def StrCpy : LibBuiltin<"string.h"> {
|
||||
let Spellings = ["strcpy"];
|
||||
let Attributes = [NoThrow];
|
||||
|
@ -1065,12 +1065,24 @@ def ext_ms_abstract_keyword : ExtWarn<
|
||||
"'abstract' keyword is a Microsoft extension">,
|
||||
InGroup<MicrosoftAbstract>;
|
||||
|
||||
def ext_relocatable_keyword : ExtWarn<
|
||||
"'%select{trivially_relocatable_if_eligible|replaceable_if_eligible}0' "
|
||||
"keyword is a C++2c extension">,
|
||||
InGroup<CXX26>;
|
||||
def warn_relocatable_keyword : Warning<
|
||||
"'%select{trivially_relocatable|replaceable}0_if_eligible' "
|
||||
"keyword is incompatible with standards before C++2c">,
|
||||
DefaultIgnore, InGroup<CXXPre26Compat>;
|
||||
|
||||
def err_access_specifier_interface : Error<
|
||||
"interface types cannot specify '%select{private|protected}0' access">;
|
||||
|
||||
def err_duplicate_class_virt_specifier : Error<
|
||||
"class already marked '%0'">;
|
||||
|
||||
def err_duplicate_class_relocation_specifier : Error<
|
||||
"class already marked '%select{trivially_relocatable_if_eligible|replaceable_if_eligible}0'">;
|
||||
|
||||
def err_duplicate_virt_specifier : Error<
|
||||
"class member already marked '%0'">;
|
||||
|
||||
|
@ -12664,6 +12664,11 @@ def err_builtin_invalid_arg_type: Error<
|
||||
"%plural{0:|: }3"
|
||||
"%plural{[0,3]:type|:types}1 (was %4)">;
|
||||
|
||||
def err_builtin_trivially_relocate_invalid_arg_type: Error <
|
||||
"first%select{||| and second}0 argument%select{|||s}0 to "
|
||||
"'__builtin_trivially_relocate' must be"
|
||||
" %select{a pointer|non-const|relocatable|of the same type}0">;
|
||||
|
||||
def err_builtin_matrix_disabled: Error<
|
||||
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;
|
||||
def err_matrix_index_not_integer: Error<
|
||||
|
@ -557,8 +557,12 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary
|
||||
// is not exposed to users.
|
||||
TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)
|
||||
TYPE_TRAIT_1(__is_bitwise_cloneable, IsBitwiseCloneable, KEYALL)
|
||||
TYPE_TRAIT_1(__builtin_is_cpp_trivially_relocatable, IsCppTriviallyRelocatable, KEYCXX)
|
||||
TYPE_TRAIT_1(__builtin_is_replaceable, IsReplaceable, KEYCXX)
|
||||
TYPE_TRAIT_1(__builtin_structured_binding_size, StructuredBindingSize, KEYCXX)
|
||||
|
||||
|
||||
|
||||
// Embarcadero Expression Traits
|
||||
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)
|
||||
EXPRESSION_TRAIT(__is_rvalue_expr, IsRValueExpr, KEYCXX)
|
||||
|
@ -264,6 +264,8 @@ class Parser : public CodeCompletionHandler {
|
||||
mutable IdentifierInfo *Ident_final;
|
||||
mutable IdentifierInfo *Ident_GNU_final;
|
||||
mutable IdentifierInfo *Ident_override;
|
||||
mutable IdentifierInfo *Ident_trivially_relocatable_if_eligible;
|
||||
mutable IdentifierInfo *Ident_replaceable_if_eligible;
|
||||
|
||||
// C++2a contextual keywords.
|
||||
mutable IdentifierInfo *Ident_import;
|
||||
@ -3196,6 +3198,16 @@ private:
|
||||
SourceLocation FriendLoc);
|
||||
|
||||
bool isCXX11FinalKeyword() const;
|
||||
|
||||
bool isCXX2CTriviallyRelocatableKeyword(Token Tok) const;
|
||||
bool isCXX2CTriviallyRelocatableKeyword() const;
|
||||
void ParseCXX2CTriviallyRelocatableSpecifier(SourceLocation &TRS);
|
||||
|
||||
bool isCXX2CReplaceableKeyword(Token Tok) const;
|
||||
bool isCXX2CReplaceableKeyword() const;
|
||||
void ParseCXX2CReplaceableSpecifier(SourceLocation &MRS);
|
||||
|
||||
bool isClassCompatibleKeyword(Token Tok) const;
|
||||
bool isClassCompatibleKeyword() const;
|
||||
|
||||
/// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to
|
||||
|
@ -4316,6 +4316,8 @@ public:
|
||||
SourceLocation FinalLoc,
|
||||
bool IsFinalSpelledSealed,
|
||||
bool IsAbstract,
|
||||
SourceLocation TriviallyRelocatable,
|
||||
SourceLocation Replaceable,
|
||||
SourceLocation LBraceLoc);
|
||||
|
||||
/// ActOnTagFinishDefinition - Invoked once we have finished parsing
|
||||
@ -4323,6 +4325,9 @@ public:
|
||||
void ActOnTagFinishDefinition(Scope *S, Decl *TagDecl,
|
||||
SourceRange BraceRange);
|
||||
|
||||
ASTContext::CXXRecordDeclRelocationInfo
|
||||
CheckCXX2CRelocatableAndReplaceable(const clang::CXXRecordDecl *D);
|
||||
|
||||
void ActOnTagFinishSkippedDefinition(SkippedDefinitionContext Context);
|
||||
|
||||
/// ActOnTagDefinitionError - Invoked when there was an unrecoverable
|
||||
@ -8637,6 +8642,18 @@ public:
|
||||
ExprResult &LHS, ExprResult &RHS,
|
||||
SourceLocation QuestionLoc);
|
||||
|
||||
//// Determines if a type is trivially relocatable
|
||||
/// according to the C++26 rules.
|
||||
// FIXME: This is in Sema because it requires
|
||||
// overload resolution, can we move to ASTContext?
|
||||
bool IsCXXTriviallyRelocatableType(QualType T);
|
||||
|
||||
//// Determines if a type is replaceable
|
||||
/// according to the C++26 rules.
|
||||
// FIXME: This is in Sema because it requires
|
||||
// overload resolution, can we move to ASTContext?
|
||||
bool IsCXXReplaceableType(QualType T);
|
||||
|
||||
/// Check the operands of ?: under C++ semantics.
|
||||
///
|
||||
/// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y
|
||||
|
@ -1688,6 +1688,24 @@ void ASTContext::getOverriddenMethods(
|
||||
Overridden.append(OverDecls.begin(), OverDecls.end());
|
||||
}
|
||||
|
||||
std::optional<ASTContext::CXXRecordDeclRelocationInfo>
|
||||
ASTContext::getRelocationInfoForCXXRecord(const CXXRecordDecl *RD) const {
|
||||
assert(RD);
|
||||
CXXRecordDecl *D = RD->getDefinition();
|
||||
auto it = RelocatableClasses.find(D);
|
||||
if (it != RelocatableClasses.end())
|
||||
return it->getSecond();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ASTContext::setRelocationInfoForCXXRecord(
|
||||
const CXXRecordDecl *RD, CXXRecordDeclRelocationInfo Info) {
|
||||
assert(RD);
|
||||
CXXRecordDecl *D = RD->getDefinition();
|
||||
assert(RelocatableClasses.find(D) == RelocatableClasses.end());
|
||||
RelocatableClasses.insert({D, Info});
|
||||
}
|
||||
|
||||
void ASTContext::addedLocalImportDecl(ImportDecl *Import) {
|
||||
assert(!Import->getNextLocalImport() &&
|
||||
"Import declaration already in the chain");
|
||||
|
@ -4491,6 +4491,7 @@ unsigned FunctionDecl::getMemoryFunctionKind() const {
|
||||
case Builtin::BImempcpy:
|
||||
return Builtin::BImempcpy;
|
||||
|
||||
case Builtin::BI__builtin_trivially_relocate:
|
||||
case Builtin::BI__builtin_memmove:
|
||||
case Builtin::BI__builtin___memmove_chk:
|
||||
case Builtin::BImemmove:
|
||||
|
@ -1556,7 +1556,6 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
|
||||
if (DD->isNoReturn())
|
||||
data().IsAnyDestructorNoReturn = true;
|
||||
}
|
||||
|
||||
if (!MD->isImplicit() && !MD->isUserProvided()) {
|
||||
// This method is user-declared but not user-provided. We can't work
|
||||
// out whether it's trivial yet (not until we get to the end of the
|
||||
@ -2144,6 +2143,12 @@ CXXDestructorDecl *CXXRecordDecl::getDestructor() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CXXRecordDecl::hasDeletedDestructor() const {
|
||||
if (const CXXDestructorDecl *D = getDestructor())
|
||||
return D->isDeleted();
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isDeclContextInNamespace(const DeclContext *DC) {
|
||||
while (!DC->isTranslationUnit()) {
|
||||
if (DC->isNamespace())
|
||||
|
@ -2896,29 +2896,6 @@ bool QualType::isTriviallyCopyConstructibleType(
|
||||
/*IsCopyConstructible=*/true);
|
||||
}
|
||||
|
||||
bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
|
||||
QualType BaseElementType = Context.getBaseElementType(*this);
|
||||
|
||||
if (BaseElementType->isIncompleteType()) {
|
||||
return false;
|
||||
} else if (!BaseElementType->isObjectType()) {
|
||||
return false;
|
||||
} else if (const auto *RD = BaseElementType->getAsRecordDecl()) {
|
||||
return RD->canPassInRegisters();
|
||||
} else if (BaseElementType.isTriviallyCopyableType(Context)) {
|
||||
return true;
|
||||
} else {
|
||||
switch (isNonTrivialToPrimitiveDestructiveMove()) {
|
||||
case PCK_Trivial:
|
||||
return !isDestructedType();
|
||||
case PCK_ARCStrong:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
|
||||
return !Context.getLangOpts().ObjCAutoRefCount &&
|
||||
Context.getLangOpts().ObjCWeak &&
|
||||
|
@ -4210,6 +4210,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
|
||||
return RValue::get(Dest, *this);
|
||||
}
|
||||
|
||||
case Builtin::BI__builtin_trivially_relocate:
|
||||
case Builtin::BImemmove:
|
||||
case Builtin::BI__builtin_memmove: {
|
||||
Address Dest = EmitPointerWithAlignment(E->getArg(0));
|
||||
|
@ -772,6 +772,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
|
||||
Builder.defineMacro("__cpp_pack_indexing", "202311L");
|
||||
Builder.defineMacro("__cpp_deleted_function", "202403L");
|
||||
Builder.defineMacro("__cpp_variadic_friend", "202403L");
|
||||
// Builder.defineMacro("__cpp_trivial_relocatability", "202502L");
|
||||
|
||||
if (LangOpts.Char8)
|
||||
Builder.defineMacro("__cpp_char8_t", "202207L");
|
||||
|
@ -2027,7 +2027,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
||||
(DSC != DeclSpecContext::DSC_association &&
|
||||
getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
|
||||
(isClassCompatibleKeyword() &&
|
||||
(NextToken().is(tok::l_brace) || NextToken().is(tok::colon)))) {
|
||||
(NextToken().is(tok::l_brace) || NextToken().is(tok::colon) ||
|
||||
isClassCompatibleKeyword(NextToken())))) {
|
||||
if (DS.isFriendSpecified()) {
|
||||
// C++ [class.friend]p2:
|
||||
// A class shall not be defined in a friend declaration.
|
||||
@ -2046,15 +2047,15 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
||||
(NextToken().is(tok::l_square) ||
|
||||
NextToken().is(tok::kw_alignas) ||
|
||||
NextToken().isRegularKeywordAttribute() ||
|
||||
isCXX11VirtSpecifier(NextToken()) != VirtSpecifiers::VS_None)) {
|
||||
isCXX11VirtSpecifier(NextToken()) != VirtSpecifiers::VS_None ||
|
||||
isCXX2CTriviallyRelocatableKeyword())) {
|
||||
// We can't tell if this is a definition or reference
|
||||
// until we skipped the 'final' and C++11 attribute specifiers.
|
||||
TentativeParsingAction PA(*this);
|
||||
|
||||
// Skip the 'final', abstract'... keywords.
|
||||
while (isClassCompatibleKeyword()) {
|
||||
while (isClassCompatibleKeyword())
|
||||
ConsumeToken();
|
||||
}
|
||||
|
||||
// Skip C++11 attribute specifiers.
|
||||
while (true) {
|
||||
@ -2696,16 +2697,76 @@ bool Parser::isCXX11FinalKeyword() const {
|
||||
Specifier == VirtSpecifiers::VS_Sealed;
|
||||
}
|
||||
|
||||
bool Parser::isCXX2CTriviallyRelocatableKeyword(Token Tok) const {
|
||||
if (!getLangOpts().CPlusPlus || Tok.isNot(tok::identifier))
|
||||
return false;
|
||||
if (!Ident_trivially_relocatable_if_eligible)
|
||||
Ident_trivially_relocatable_if_eligible =
|
||||
&PP.getIdentifierTable().get("trivially_relocatable_if_eligible");
|
||||
IdentifierInfo *II = Tok.getIdentifierInfo();
|
||||
return II == Ident_trivially_relocatable_if_eligible;
|
||||
}
|
||||
|
||||
bool Parser::isCXX2CTriviallyRelocatableKeyword() const {
|
||||
return isCXX2CTriviallyRelocatableKeyword(Tok);
|
||||
}
|
||||
|
||||
void Parser::ParseCXX2CTriviallyRelocatableSpecifier(SourceLocation &TRS) {
|
||||
assert(isCXX2CTriviallyRelocatableKeyword() &&
|
||||
"expected a trivially_relocatable specifier");
|
||||
|
||||
Diag(Tok.getLocation(), getLangOpts().CPlusPlus26
|
||||
? diag::warn_relocatable_keyword
|
||||
: diag::ext_relocatable_keyword)
|
||||
<< /*relocatable*/ 0;
|
||||
|
||||
TRS = ConsumeToken();
|
||||
}
|
||||
|
||||
bool Parser::isCXX2CReplaceableKeyword(Token Tok) const {
|
||||
if (!getLangOpts().CPlusPlus || Tok.isNot(tok::identifier))
|
||||
return false;
|
||||
if (!Ident_replaceable_if_eligible)
|
||||
Ident_replaceable_if_eligible =
|
||||
&PP.getIdentifierTable().get("replaceable_if_eligible");
|
||||
IdentifierInfo *II = Tok.getIdentifierInfo();
|
||||
return II == Ident_replaceable_if_eligible;
|
||||
}
|
||||
|
||||
bool Parser::isCXX2CReplaceableKeyword() const {
|
||||
return isCXX2CReplaceableKeyword(Tok);
|
||||
}
|
||||
|
||||
void Parser::ParseCXX2CReplaceableSpecifier(SourceLocation &MRS) {
|
||||
assert(isCXX2CReplaceableKeyword() &&
|
||||
"expected a replaceable_if_eligible specifier");
|
||||
|
||||
Diag(Tok.getLocation(), getLangOpts().CPlusPlus26
|
||||
? diag::warn_relocatable_keyword
|
||||
: diag::ext_relocatable_keyword)
|
||||
<< /*replaceable*/ 1;
|
||||
|
||||
MRS = ConsumeToken();
|
||||
}
|
||||
|
||||
/// isClassCompatibleKeyword - Determine whether the next token is a C++11
|
||||
/// 'final' or Microsoft 'sealed' or 'abstract' contextual keywords.
|
||||
bool Parser::isClassCompatibleKeyword() const {
|
||||
VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier();
|
||||
/// 'final', a C++26 'trivially_relocatable_if_eligible',
|
||||
/// 'replaceable_if_eligible', or Microsoft 'sealed' or 'abstract' contextual
|
||||
/// keyword.
|
||||
bool Parser::isClassCompatibleKeyword(Token Tok) const {
|
||||
if (isCXX2CTriviallyRelocatableKeyword(Tok) || isCXX2CReplaceableKeyword(Tok))
|
||||
return true;
|
||||
VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok);
|
||||
return Specifier == VirtSpecifiers::VS_Final ||
|
||||
Specifier == VirtSpecifiers::VS_GNU_Final ||
|
||||
Specifier == VirtSpecifiers::VS_Sealed ||
|
||||
Specifier == VirtSpecifiers::VS_Abstract;
|
||||
}
|
||||
|
||||
bool Parser::isClassCompatibleKeyword() const {
|
||||
return isClassCompatibleKeyword(Tok);
|
||||
}
|
||||
|
||||
/// Parse a C++ member-declarator up to, but not including, the optional
|
||||
/// brace-or-equal-initializer or pure-specifier.
|
||||
bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
|
||||
@ -3584,21 +3645,19 @@ void Parser::SkipCXXMemberSpecification(SourceLocation RecordLoc,
|
||||
SourceLocation AttrFixitLoc,
|
||||
unsigned TagType, Decl *TagDecl) {
|
||||
// Skip the optional 'final' keyword.
|
||||
if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) {
|
||||
assert(isCXX11FinalKeyword() && "not a class definition");
|
||||
while (isClassCompatibleKeyword())
|
||||
ConsumeToken();
|
||||
|
||||
// Diagnose any C++11 attributes after 'final' keyword.
|
||||
// We deliberately discard these attributes.
|
||||
ParsedAttributes Attrs(AttrFactory);
|
||||
CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc);
|
||||
// Diagnose any C++11 attributes after 'final' keyword.
|
||||
// We deliberately discard these attributes.
|
||||
ParsedAttributes Attrs(AttrFactory);
|
||||
CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc);
|
||||
|
||||
// This can only happen if we had malformed misplaced attributes;
|
||||
// we only get called if there is a colon or left-brace after the
|
||||
// attributes.
|
||||
if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_brace))
|
||||
return;
|
||||
}
|
||||
// This can only happen if we had malformed misplaced attributes;
|
||||
// we only get called if there is a colon or left-brace after the
|
||||
// attributes.
|
||||
if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_brace))
|
||||
return;
|
||||
|
||||
// Skip the base clauses. This requires actually parsing them, because
|
||||
// otherwise we can't be sure where they end (a left brace may appear
|
||||
@ -3812,13 +3871,38 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
|
||||
SourceLocation AbstractLoc;
|
||||
bool IsFinalSpelledSealed = false;
|
||||
bool IsAbstract = false;
|
||||
SourceLocation TriviallyRelocatable;
|
||||
SourceLocation Replaceable;
|
||||
|
||||
// Parse the optional 'final' keyword.
|
||||
if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) {
|
||||
while (true) {
|
||||
VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok);
|
||||
if (Specifier == VirtSpecifiers::VS_None)
|
||||
if (Specifier == VirtSpecifiers::VS_None) {
|
||||
if (isCXX2CTriviallyRelocatableKeyword(Tok)) {
|
||||
if (TriviallyRelocatable.isValid()) {
|
||||
auto Skipped = Tok;
|
||||
ConsumeToken();
|
||||
Diag(Skipped, diag::err_duplicate_class_relocation_specifier)
|
||||
<< /*trivial_relocatable*/ 0 << TriviallyRelocatable;
|
||||
} else {
|
||||
ParseCXX2CTriviallyRelocatableSpecifier(TriviallyRelocatable);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isCXX2CReplaceableKeyword(Tok)) {
|
||||
if (Replaceable.isValid()) {
|
||||
auto Skipped = Tok;
|
||||
ConsumeToken();
|
||||
Diag(Skipped, diag::err_duplicate_class_relocation_specifier)
|
||||
<< /*replaceable*/ 1 << Replaceable;
|
||||
} else {
|
||||
ParseCXX2CReplaceableSpecifier(Replaceable);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isCXX11FinalKeyword()) {
|
||||
if (FinalLoc.isValid()) {
|
||||
auto Skipped = ConsumeToken();
|
||||
@ -3854,7 +3938,8 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
|
||||
else if (Specifier == VirtSpecifiers::VS_GNU_Final)
|
||||
Diag(FinalLoc, diag::ext_warn_gnu_final);
|
||||
}
|
||||
assert((FinalLoc.isValid() || AbstractLoc.isValid()) &&
|
||||
assert((FinalLoc.isValid() || AbstractLoc.isValid() ||
|
||||
TriviallyRelocatable.isValid() || Replaceable.isValid()) &&
|
||||
"not a class definition");
|
||||
|
||||
// Parse any C++11 attributes after 'final' keyword.
|
||||
@ -3927,9 +4012,9 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
|
||||
T.consumeOpen();
|
||||
|
||||
if (TagDecl)
|
||||
Actions.ActOnStartCXXMemberDeclarations(getCurScope(), TagDecl, FinalLoc,
|
||||
IsFinalSpelledSealed, IsAbstract,
|
||||
T.getOpenLocation());
|
||||
Actions.ActOnStartCXXMemberDeclarations(
|
||||
getCurScope(), TagDecl, FinalLoc, IsFinalSpelledSealed, IsAbstract,
|
||||
TriviallyRelocatable, Replaceable, T.getOpenLocation());
|
||||
|
||||
// C++ 11p3: Members of a class defined with the keyword class are private
|
||||
// by default. Members of a class defined with the keywords struct or union
|
||||
|
@ -537,6 +537,8 @@ void Parser::Initialize() {
|
||||
Ident_sealed = nullptr;
|
||||
Ident_abstract = nullptr;
|
||||
Ident_override = nullptr;
|
||||
Ident_trivially_relocatable_if_eligible = nullptr;
|
||||
Ident_replaceable_if_eligible = nullptr;
|
||||
Ident_GNU_final = nullptr;
|
||||
Ident_import = nullptr;
|
||||
Ident_module = nullptr;
|
||||
|
@ -1919,6 +1919,54 @@ static ExprResult BuiltinIsWithinLifetime(Sema &S, CallExpr *TheCall) {
|
||||
<< 0;
|
||||
return ExprError();
|
||||
}
|
||||
return TheCall;
|
||||
}
|
||||
|
||||
static ExprResult BuiltinTriviallyRelocate(Sema &S, CallExpr *TheCall) {
|
||||
if (S.checkArgCount(TheCall, 3))
|
||||
return ExprError();
|
||||
|
||||
QualType Dest = TheCall->getArg(0)->getType();
|
||||
if (!Dest->isPointerType() || Dest.getCVRQualifiers() != 0) {
|
||||
S.Diag(TheCall->getArg(0)->getExprLoc(),
|
||||
diag::err_builtin_trivially_relocate_invalid_arg_type)
|
||||
<< /*a pointer*/ 0;
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
QualType T = Dest->getPointeeType();
|
||||
if (S.RequireCompleteType(TheCall->getBeginLoc(), T,
|
||||
diag::err_incomplete_type))
|
||||
return ExprError();
|
||||
|
||||
if (T.isConstQualified() || !S.IsCXXTriviallyRelocatableType(T) ||
|
||||
T->isIncompleteArrayType()) {
|
||||
S.Diag(TheCall->getArg(0)->getExprLoc(),
|
||||
diag::err_builtin_trivially_relocate_invalid_arg_type)
|
||||
<< (T.isConstQualified() ? /*non-const*/ 1 : /*relocatable*/ 2);
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
TheCall->setType(Dest);
|
||||
|
||||
QualType Src = TheCall->getArg(1)->getType();
|
||||
if (Src.getCanonicalType() != Dest.getCanonicalType()) {
|
||||
S.Diag(TheCall->getArg(1)->getExprLoc(),
|
||||
diag::err_builtin_trivially_relocate_invalid_arg_type)
|
||||
<< /*the same*/ 3;
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
Expr *SizeExpr = TheCall->getArg(2);
|
||||
ExprResult Size = S.DefaultLvalueConversion(SizeExpr);
|
||||
if (Size.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
Size = S.tryConvertExprToType(Size.get(), S.getASTContext().getSizeType());
|
||||
if (Size.isInvalid())
|
||||
return ExprError();
|
||||
SizeExpr = Size.get();
|
||||
TheCall->setArg(2, SizeExpr);
|
||||
|
||||
return TheCall;
|
||||
}
|
||||
@ -2384,6 +2432,9 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
|
||||
return BuiltinLaunder(*this, TheCall);
|
||||
case Builtin::BI__builtin_is_within_lifetime:
|
||||
return BuiltinIsWithinLifetime(*this, TheCall);
|
||||
case Builtin::BI__builtin_trivially_relocate:
|
||||
return BuiltinTriviallyRelocate(*this, TheCall);
|
||||
|
||||
case Builtin::BI__sync_fetch_and_add:
|
||||
case Builtin::BI__sync_fetch_and_add_1:
|
||||
case Builtin::BI__sync_fetch_and_add_2:
|
||||
|
@ -18480,11 +18480,10 @@ bool Sema::ActOnDuplicateDefinition(Scope *S, Decl *Prev,
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD,
|
||||
SourceLocation FinalLoc,
|
||||
bool IsFinalSpelledSealed,
|
||||
bool IsAbstract,
|
||||
SourceLocation LBraceLoc) {
|
||||
void Sema::ActOnStartCXXMemberDeclarations(
|
||||
Scope *S, Decl *TagD, SourceLocation FinalLoc, bool IsFinalSpelledSealed,
|
||||
bool IsAbstract, SourceLocation TriviallyRelocatable,
|
||||
SourceLocation Replaceable, SourceLocation LBraceLoc) {
|
||||
AdjustDeclIfTemplate(TagD);
|
||||
CXXRecordDecl *Record = cast<CXXRecordDecl>(TagD);
|
||||
|
||||
@ -18502,6 +18501,14 @@ void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD,
|
||||
? FinalAttr::Keyword_sealed
|
||||
: FinalAttr::Keyword_final));
|
||||
}
|
||||
|
||||
if (TriviallyRelocatable.isValid())
|
||||
Record->addAttr(
|
||||
TriviallyRelocatableAttr::Create(Context, TriviallyRelocatable));
|
||||
|
||||
if (Replaceable.isValid())
|
||||
Record->addAttr(ReplaceableAttr::Create(Context, Replaceable));
|
||||
|
||||
// C++ [class]p2:
|
||||
// [...] The class-name is also inserted into the scope of the
|
||||
// class itself; this is known as the injected-class-name. For
|
||||
|
@ -7369,6 +7369,279 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
|
||||
CheckMismatchedTypeAwareAllocators(OO_Array_New, OO_Array_Delete);
|
||||
}
|
||||
|
||||
static CXXMethodDecl *LookupSpecialMemberFromXValue(Sema &SemaRef,
|
||||
const CXXRecordDecl *RD,
|
||||
bool Assign) {
|
||||
RD = RD->getDefinition();
|
||||
SourceLocation LookupLoc = RD->getLocation();
|
||||
|
||||
CanQualType CanTy = SemaRef.getASTContext().getCanonicalType(
|
||||
SemaRef.getASTContext().getTagDeclType(RD));
|
||||
DeclarationName Name;
|
||||
Expr *Arg = nullptr;
|
||||
unsigned NumArgs;
|
||||
|
||||
QualType ArgType = CanTy;
|
||||
ExprValueKind VK = clang::VK_XValue;
|
||||
|
||||
if (Assign)
|
||||
Name =
|
||||
SemaRef.getASTContext().DeclarationNames.getCXXOperatorName(OO_Equal);
|
||||
else
|
||||
Name =
|
||||
SemaRef.getASTContext().DeclarationNames.getCXXConstructorName(CanTy);
|
||||
|
||||
OpaqueValueExpr FakeArg(LookupLoc, ArgType, VK);
|
||||
NumArgs = 1;
|
||||
Arg = &FakeArg;
|
||||
|
||||
// Create the object argument
|
||||
QualType ThisTy = CanTy;
|
||||
Expr::Classification Classification =
|
||||
OpaqueValueExpr(LookupLoc, ThisTy, VK_LValue)
|
||||
.Classify(SemaRef.getASTContext());
|
||||
|
||||
// Now we perform lookup on the name we computed earlier and do overload
|
||||
// resolution. Lookup is only performed directly into the class since there
|
||||
// will always be a (possibly implicit) declaration to shadow any others.
|
||||
OverloadCandidateSet OCS(LookupLoc, OverloadCandidateSet::CSK_Normal);
|
||||
DeclContext::lookup_result R = RD->lookup(Name);
|
||||
|
||||
if (R.empty())
|
||||
return nullptr;
|
||||
|
||||
// Copy the candidates as our processing of them may load new declarations
|
||||
// from an external source and invalidate lookup_result.
|
||||
SmallVector<NamedDecl *, 8> Candidates(R.begin(), R.end());
|
||||
|
||||
for (NamedDecl *CandDecl : Candidates) {
|
||||
if (CandDecl->isInvalidDecl())
|
||||
continue;
|
||||
|
||||
DeclAccessPair Cand = DeclAccessPair::make(CandDecl, clang::AS_none);
|
||||
auto CtorInfo = getConstructorInfo(Cand);
|
||||
if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
|
||||
if (Assign)
|
||||
SemaRef.AddMethodCandidate(M, Cand, const_cast<CXXRecordDecl *>(RD),
|
||||
ThisTy, Classification,
|
||||
llvm::ArrayRef(&Arg, NumArgs), OCS, true);
|
||||
else {
|
||||
assert(CtorInfo);
|
||||
SemaRef.AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
|
||||
llvm::ArrayRef(&Arg, NumArgs), OCS,
|
||||
/*SuppressUserConversions*/ true);
|
||||
}
|
||||
} else if (FunctionTemplateDecl *Tmpl =
|
||||
dyn_cast<FunctionTemplateDecl>(Cand->getUnderlyingDecl())) {
|
||||
if (Assign)
|
||||
SemaRef.AddMethodTemplateCandidate(
|
||||
Tmpl, Cand, const_cast<CXXRecordDecl *>(RD), nullptr, ThisTy,
|
||||
Classification, llvm::ArrayRef(&Arg, NumArgs), OCS, true);
|
||||
else {
|
||||
assert(CtorInfo);
|
||||
SemaRef.AddTemplateOverloadCandidate(
|
||||
CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
|
||||
llvm::ArrayRef(&Arg, NumArgs), OCS, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OverloadCandidateSet::iterator Best;
|
||||
switch (OCS.BestViableFunction(SemaRef, LookupLoc, Best)) {
|
||||
case OR_Success:
|
||||
return cast<CXXMethodDecl>(Best->Function);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool hasSuitableConstructorForRelocation(Sema &SemaRef,
|
||||
const CXXRecordDecl *D,
|
||||
bool AllowUserDefined) {
|
||||
assert(D->hasDefinition() && !D->isInvalidDecl());
|
||||
|
||||
if (D->hasSimpleMoveConstructor() || D->hasSimpleCopyConstructor())
|
||||
return true;
|
||||
|
||||
CXXMethodDecl *Decl =
|
||||
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false);
|
||||
return Decl && Decl->isUserProvided() == AllowUserDefined;
|
||||
}
|
||||
|
||||
static bool hasSuitableMoveAssignmentOperatorForRelocation(
|
||||
Sema &SemaRef, const CXXRecordDecl *D, bool AllowUserDefined) {
|
||||
assert(D->hasDefinition() && !D->isInvalidDecl());
|
||||
|
||||
if (D->hasSimpleMoveAssignment() || D->hasSimpleCopyAssignment())
|
||||
return true;
|
||||
|
||||
CXXMethodDecl *Decl =
|
||||
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
|
||||
if (!Decl)
|
||||
return false;
|
||||
|
||||
return Decl && Decl->isUserProvided() == AllowUserDefined;
|
||||
}
|
||||
|
||||
// [C++26][class.prop]
|
||||
// A class C is default-movable if
|
||||
// - overload resolution for direct-initializing an object of type C
|
||||
// from an xvalue of type C selects a constructor that is a direct member of C
|
||||
// and is neither user-provided nor deleted,
|
||||
// - overload resolution for assigning to an lvalue of type C from an xvalue of
|
||||
// type C selects an assignment operator function that is a direct member of C
|
||||
// and is neither user-provided nor deleted, and C has a destructor that is
|
||||
// neither user-provided nor deleted.
|
||||
static bool IsDefaultMovable(Sema &SemaRef, const CXXRecordDecl *D) {
|
||||
if (!hasSuitableConstructorForRelocation(SemaRef, D,
|
||||
/*AllowUserDefined=*/false))
|
||||
return false;
|
||||
|
||||
if (!hasSuitableMoveAssignmentOperatorForRelocation(
|
||||
SemaRef, D, /*AllowUserDefined=*/false))
|
||||
return false;
|
||||
|
||||
CXXDestructorDecl *Dtr = D->getDestructor();
|
||||
|
||||
if (!Dtr)
|
||||
return true;
|
||||
|
||||
if (Dtr->isUserProvided() && (!Dtr->isDefaulted() || Dtr->isDeleted()))
|
||||
return false;
|
||||
|
||||
return !Dtr->isDeleted();
|
||||
}
|
||||
|
||||
// [C++26][class.prop]
|
||||
// A class is eligible for trivial relocation unless it...
|
||||
static bool IsEligibleForTrivialRelocation(Sema &SemaRef,
|
||||
const CXXRecordDecl *D) {
|
||||
|
||||
for (const CXXBaseSpecifier &B : D->bases()) {
|
||||
const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
|
||||
if (!BaseDecl)
|
||||
continue;
|
||||
// ... has any virtual base classes
|
||||
// ... has a base class that is not a trivially relocatable class
|
||||
if (B.isVirtual() || (!BaseDecl->isDependentType() &&
|
||||
!SemaRef.IsCXXTriviallyRelocatableType(B.getType())))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FieldDecl *Field : D->fields()) {
|
||||
if (Field->getType()->isDependentType())
|
||||
continue;
|
||||
if (Field->getType()->isReferenceType())
|
||||
continue;
|
||||
// ... has a non-static data member of an object type that is not
|
||||
// of a trivially relocatable type
|
||||
if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
|
||||
return false;
|
||||
}
|
||||
return !D->hasDeletedDestructor();
|
||||
}
|
||||
|
||||
// [C++26][class.prop]
|
||||
// A class C is eligible for replacement unless
|
||||
static bool IsEligibleForReplacement(Sema &SemaRef, const CXXRecordDecl *D) {
|
||||
|
||||
for (const CXXBaseSpecifier &B : D->bases()) {
|
||||
const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
|
||||
if (!BaseDecl)
|
||||
continue;
|
||||
// it has a base class that is not a replaceable class
|
||||
if (!BaseDecl->isDependentType() &&
|
||||
!SemaRef.IsCXXReplaceableType(B.getType()))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FieldDecl *Field : D->fields()) {
|
||||
if (Field->getType()->isDependentType())
|
||||
continue;
|
||||
|
||||
// it has a non-static data member that is not of a replaceable type,
|
||||
if (!SemaRef.IsCXXReplaceableType(Field->getType()))
|
||||
return false;
|
||||
}
|
||||
return !D->hasDeletedDestructor();
|
||||
}
|
||||
|
||||
ASTContext::CXXRecordDeclRelocationInfo
|
||||
Sema::CheckCXX2CRelocatableAndReplaceable(const CXXRecordDecl *D) {
|
||||
ASTContext::CXXRecordDeclRelocationInfo Info{false, false};
|
||||
|
||||
if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
|
||||
return Info;
|
||||
|
||||
assert(D->hasDefinition());
|
||||
|
||||
// This is part of "eligible for replacement", however we defer it
|
||||
// to avoid extraneous computations.
|
||||
auto HasSuitableSMP = [&] {
|
||||
return hasSuitableConstructorForRelocation(*this, D,
|
||||
/*AllowUserDefined=*/true) &&
|
||||
hasSuitableMoveAssignmentOperatorForRelocation(
|
||||
*this, D, /*AllowUserDefined=*/true);
|
||||
};
|
||||
|
||||
auto IsUnion = [&, Is = std::optional<bool>{}]() mutable {
|
||||
if (!Is.has_value())
|
||||
Is = D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
|
||||
!D->hasUserDeclaredCopyAssignment() &&
|
||||
!D->hasUserDeclaredMoveOperation() &&
|
||||
!D->hasUserDeclaredDestructor();
|
||||
return *Is;
|
||||
};
|
||||
|
||||
auto IsDefaultMovable = [&, Is = std::optional<bool>{}]() mutable {
|
||||
if (!Is.has_value())
|
||||
Is = ::IsDefaultMovable(*this, D);
|
||||
return *Is;
|
||||
};
|
||||
|
||||
Info.IsRelocatable = [&] {
|
||||
if (D->isDependentType())
|
||||
return false;
|
||||
|
||||
// if it is eligible for trivial relocation
|
||||
if (!IsEligibleForTrivialRelocation(*this, D))
|
||||
return false;
|
||||
|
||||
// has the trivially_relocatable_if_eligible class-property-specifier,
|
||||
if (D->hasAttr<TriviallyRelocatableAttr>())
|
||||
return true;
|
||||
|
||||
// is a union with no user-declared special member functions, or
|
||||
if (IsUnion())
|
||||
return true;
|
||||
|
||||
// is default-movable.
|
||||
return IsDefaultMovable();
|
||||
}();
|
||||
|
||||
Info.IsReplaceable = [&] {
|
||||
if (D->isDependentType())
|
||||
return false;
|
||||
|
||||
// A class C is a replaceable class if it is eligible for replacement
|
||||
if (!IsEligibleForReplacement(*this, D))
|
||||
return false;
|
||||
|
||||
// has the replaceable_if_eligible class-property-specifier
|
||||
if (D->hasAttr<ReplaceableAttr>())
|
||||
return HasSuitableSMP();
|
||||
|
||||
// is a union with no user-declared special member functions, or
|
||||
if (IsUnion())
|
||||
return HasSuitableSMP();
|
||||
|
||||
// is default-movable.
|
||||
return IsDefaultMovable();
|
||||
}();
|
||||
|
||||
return Info;
|
||||
}
|
||||
|
||||
/// Look up the special member function that would be called by a special
|
||||
/// member function for a subobject of class type.
|
||||
///
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "clang/AST/ASTLambda.h"
|
||||
#include "clang/AST/CXXInheritance.h"
|
||||
#include "clang/AST/CharUnits.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/DynamicRecursiveASTVisitor.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
@ -5444,6 +5445,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
|
||||
// impose the same constraints.
|
||||
case UTT_IsTriviallyRelocatable:
|
||||
case UTT_IsTriviallyEqualityComparable:
|
||||
case UTT_IsCppTriviallyRelocatable:
|
||||
case UTT_IsReplaceable:
|
||||
case UTT_CanPassInRegs:
|
||||
// Per the GCC type traits documentation, T shall be a complete type, cv void,
|
||||
// or an array of unknown bound. But GCC actually imposes the same constraints
|
||||
@ -5588,6 +5591,100 @@ static bool isTriviallyEqualityComparableType(Sema &S, QualType Type, SourceLoca
|
||||
CanonicalType, /*CheckIfTriviallyCopyable=*/false);
|
||||
}
|
||||
|
||||
static bool IsCXXTriviallyRelocatableType(Sema &S, const CXXRecordDecl *RD) {
|
||||
if (std::optional<ASTContext::CXXRecordDeclRelocationInfo> Info =
|
||||
S.getASTContext().getRelocationInfoForCXXRecord(RD))
|
||||
return Info->IsRelocatable;
|
||||
ASTContext::CXXRecordDeclRelocationInfo Info =
|
||||
S.CheckCXX2CRelocatableAndReplaceable(RD);
|
||||
S.getASTContext().setRelocationInfoForCXXRecord(RD, Info);
|
||||
return Info.IsRelocatable;
|
||||
}
|
||||
|
||||
bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
|
||||
|
||||
QualType BaseElementType = getASTContext().getBaseElementType(Type);
|
||||
|
||||
if (Type->isVariableArrayType())
|
||||
return false;
|
||||
|
||||
if (BaseElementType.hasNonTrivialObjCLifetime())
|
||||
return false;
|
||||
|
||||
if (BaseElementType.hasAddressDiscriminatedPointerAuth())
|
||||
return false;
|
||||
|
||||
if (BaseElementType->isIncompleteType())
|
||||
return false;
|
||||
|
||||
if (BaseElementType->isScalarType() || BaseElementType->isVectorType())
|
||||
return true;
|
||||
|
||||
if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
|
||||
return ::IsCXXTriviallyRelocatableType(*this, RD);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsCXXReplaceableType(Sema &S, const CXXRecordDecl *RD) {
|
||||
if (std::optional<ASTContext::CXXRecordDeclRelocationInfo> Info =
|
||||
S.getASTContext().getRelocationInfoForCXXRecord(RD))
|
||||
return Info->IsReplaceable;
|
||||
ASTContext::CXXRecordDeclRelocationInfo Info =
|
||||
S.CheckCXX2CRelocatableAndReplaceable(RD);
|
||||
S.getASTContext().setRelocationInfoForCXXRecord(RD, Info);
|
||||
return Info.IsReplaceable;
|
||||
}
|
||||
|
||||
bool Sema::IsCXXReplaceableType(QualType Type) {
|
||||
if (Type.isConstQualified() || Type.isVolatileQualified())
|
||||
return false;
|
||||
|
||||
if (Type->isVariableArrayType())
|
||||
return false;
|
||||
|
||||
QualType BaseElementType =
|
||||
getASTContext().getBaseElementType(Type.getUnqualifiedType());
|
||||
if (BaseElementType->isIncompleteType())
|
||||
return false;
|
||||
if (BaseElementType->isScalarType())
|
||||
return true;
|
||||
if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
|
||||
return ::IsCXXReplaceableType(*this, RD);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) {
|
||||
QualType BaseElementType = SemaRef.getASTContext().getBaseElementType(T);
|
||||
|
||||
if (BaseElementType->isIncompleteType())
|
||||
return false;
|
||||
if (!BaseElementType->isObjectType())
|
||||
return false;
|
||||
|
||||
if (T.hasAddressDiscriminatedPointerAuth())
|
||||
return false;
|
||||
|
||||
if (const auto *RD = BaseElementType->getAsCXXRecordDecl();
|
||||
RD && !RD->isPolymorphic() && IsCXXTriviallyRelocatableType(SemaRef, RD))
|
||||
return true;
|
||||
|
||||
if (const auto *RD = BaseElementType->getAsRecordDecl())
|
||||
return RD->canPassInRegisters();
|
||||
|
||||
if (BaseElementType.isTriviallyCopyableType(SemaRef.getASTContext()))
|
||||
return true;
|
||||
|
||||
switch (T.isNonTrivialToPrimitiveDestructiveMove()) {
|
||||
case QualType::PCK_Trivial:
|
||||
return !T.isDestructedType();
|
||||
case QualType::PCK_ARCStrong:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
|
||||
SourceLocation KeyLoc,
|
||||
TypeSourceInfo *TInfo) {
|
||||
@ -6007,9 +6104,13 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
|
||||
case UTT_HasUniqueObjectRepresentations:
|
||||
return C.hasUniqueObjectRepresentations(T);
|
||||
case UTT_IsTriviallyRelocatable:
|
||||
return T.isTriviallyRelocatableType(C);
|
||||
return IsTriviallyRelocatableType(Self, T);
|
||||
case UTT_IsBitwiseCloneable:
|
||||
return T.isBitwiseCloneableType(C);
|
||||
case UTT_IsCppTriviallyRelocatable:
|
||||
return Self.IsCXXTriviallyRelocatableType(T);
|
||||
case UTT_IsReplaceable:
|
||||
return Self.IsCXXReplaceableType(T);
|
||||
case UTT_CanPassInRegs:
|
||||
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl(); RD && !T.hasQualifiers())
|
||||
return RD->canPassInRegisters();
|
||||
|
16
clang/test/CodeGenCXX/cxx2c-trivially-relocatable.cpp
Normal file
16
clang/test/CodeGenCXX/cxx2c-trivially-relocatable.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
struct S trivially_relocatable_if_eligible {
|
||||
S(const S&);
|
||||
~S();
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
// CHECK: @_Z4testP1SS0_
|
||||
// CHECK: call void @llvm.memmove.p0.p0.i64
|
||||
// CHECK-NOT: __builtin
|
||||
// CHECK: ret
|
||||
void test(S* source, S* dest) {
|
||||
__builtin_trivially_relocate(dest, source, 1);
|
||||
};
|
42
clang/test/Parser/cxx2c-trivially-relocatable.cpp
Normal file
42
clang/test/Parser/cxx2c-trivially-relocatable.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
// RUN: %clang_cc1 -std=c++03 -verify=expected,cxx11,cxx03 -fsyntax-only %s
|
||||
// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx11 -fsyntax-only %s
|
||||
// RUN: %clang_cc1 -std=c++2c -verify=expected -fsyntax-only %s
|
||||
|
||||
|
||||
class A trivially_relocatable_if_eligible {};
|
||||
// cxx11-warning@-1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
class E final trivially_relocatable_if_eligible {};
|
||||
// cxx11-warning@-1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx03-warning@-2 {{'final' keyword is a C++11 extension}}
|
||||
class G trivially_relocatable_if_eligible final{};
|
||||
// cxx11-warning@-1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx03-warning@-2 {{'final' keyword is a C++11 extension}}
|
||||
class I trivially_relocatable_if_eligible trivially_relocatable_if_eligible final {};
|
||||
// expected-error@-1 {{class already marked 'trivially_relocatable_if_eligible'}}
|
||||
// cxx11-warning@-2 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx03-warning@-3 {{'final' keyword is a C++11 extension}}
|
||||
class trivially_relocatable_if_eligible trivially_relocatable_if_eligible {};
|
||||
// cxx11-warning@-1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
class J replaceable_if_eligible{};
|
||||
// cxx11-warning@-1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
|
||||
class K replaceable_if_eligible replaceable_if_eligible {};
|
||||
// expected-error@-1 {{class already marked 'replaceable_if_eligible'}}
|
||||
// cxx11-warning@-2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
|
||||
class replaceable_if_eligible replaceable_if_eligible {};
|
||||
// cxx11-warning@-1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
|
||||
class L replaceable_if_eligible trivially_relocatable_if_eligible final {};
|
||||
// cxx11-warning@-1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx11-warning@-2 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx03-warning@-3 {{'final' keyword is a C++11 extension}}
|
||||
class M replaceable_if_eligible final trivially_relocatable_if_eligible {};
|
||||
// cxx11-warning@-1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx11-warning@-2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx03-warning@-3 {{'final' keyword is a C++11 extension}}
|
||||
class N final trivially_relocatable_if_eligible replaceable_if_eligible {};
|
||||
// cxx11-warning@-1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx11-warning@-2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx03-warning@-3 {{'final' keyword is a C++11 extension}}
|
||||
class O trivially_relocatable_if_eligible replaceable_if_eligible final {};
|
||||
// cxx11-warning@-1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx11-warning@-2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
|
||||
// cxx03-warning@-3 {{'final' keyword is a C++11 extension}}
|
@ -81,7 +81,7 @@ struct __attribute__((trivial_abi)) S10 {
|
||||
|
||||
S10<int *> p1;
|
||||
static_assert(__is_trivially_relocatable(S10<int>), "");
|
||||
static_assert(!__is_trivially_relocatable(S10<S3>), "");
|
||||
static_assert(__is_trivially_relocatable(S10<S3>), "");
|
||||
|
||||
template <class T>
|
||||
struct S14 {
|
||||
@ -94,14 +94,14 @@ struct __attribute__((trivial_abi)) S15 : S14<T> {
|
||||
|
||||
S15<int> s15;
|
||||
static_assert(__is_trivially_relocatable(S15<int>), "");
|
||||
static_assert(!__is_trivially_relocatable(S15<S3>), "");
|
||||
static_assert(__is_trivially_relocatable(S15<S3>), "");
|
||||
|
||||
template <class T>
|
||||
struct __attribute__((trivial_abi)) S16 {
|
||||
S14<T> a;
|
||||
};
|
||||
static_assert(__is_trivially_relocatable(S16<int>), "");
|
||||
static_assert(!__is_trivially_relocatable(S16<S3>), "");
|
||||
static_assert(__is_trivially_relocatable(S16<S3>), "");
|
||||
|
||||
S16<int> s16;
|
||||
|
||||
|
365
clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
Normal file
365
clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
Normal file
@ -0,0 +1,365 @@
|
||||
// RUN: %clang_cc1 -std=c++2c -verify %s
|
||||
|
||||
class Trivial {};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(Trivial));
|
||||
struct NonRelocatable {
|
||||
~NonRelocatable();
|
||||
};
|
||||
static NonRelocatable NonRelocatable_g;
|
||||
|
||||
class A trivially_relocatable_if_eligible {};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(A));
|
||||
|
||||
|
||||
class B trivially_relocatable_if_eligible : Trivial{};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(B));
|
||||
|
||||
class C trivially_relocatable_if_eligible {
|
||||
int a;
|
||||
void* b;
|
||||
int c[3];
|
||||
Trivial d[3];
|
||||
NonRelocatable& e = NonRelocatable_g;
|
||||
};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(C));
|
||||
|
||||
|
||||
class D trivially_relocatable_if_eligible : Trivial {};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(D));
|
||||
|
||||
|
||||
class E trivially_relocatable_if_eligible : virtual Trivial {};
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(E));
|
||||
|
||||
|
||||
class F trivially_relocatable_if_eligible : NonRelocatable {};
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(F));
|
||||
|
||||
class G trivially_relocatable_if_eligible {
|
||||
G(G&&);
|
||||
};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(G));
|
||||
|
||||
class H trivially_relocatable_if_eligible {
|
||||
~H();
|
||||
};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(H));
|
||||
|
||||
class I trivially_relocatable_if_eligible {
|
||||
NonRelocatable a;
|
||||
NonRelocatable b[1];
|
||||
const NonRelocatable c;
|
||||
const NonRelocatable d[1];
|
||||
};
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(I));
|
||||
|
||||
|
||||
class J trivially_relocatable_if_eligible: virtual Trivial, NonRelocatable {
|
||||
NonRelocatable a;
|
||||
};
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(J));
|
||||
|
||||
|
||||
struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}}
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(Incomplete)); // expected-error {{incomplete type 'Incomplete' used in type trait expression}}
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(int));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(void*));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(int&));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(Trivial&));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(const Trivial));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(Trivial[1]));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(Trivial[]));
|
||||
|
||||
struct WithConst {
|
||||
const int i;
|
||||
};
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(WithConst));
|
||||
|
||||
struct WithConstExplicit trivially_relocatable_if_eligible {
|
||||
const int i;
|
||||
};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(WithConstExplicit));
|
||||
|
||||
struct UserDtr {
|
||||
~UserDtr();
|
||||
};
|
||||
|
||||
struct DefaultedDtr {
|
||||
~DefaultedDtr() = default;
|
||||
};
|
||||
struct UserMoveWithDefaultCopy {
|
||||
UserMoveWithDefaultCopy(UserMoveWithDefaultCopy&&);
|
||||
UserMoveWithDefaultCopy(const UserMoveWithDefaultCopy&) = default;
|
||||
};
|
||||
|
||||
struct UserMove{
|
||||
UserMove(UserMove&&);
|
||||
};
|
||||
|
||||
struct UserMoveDefault{
|
||||
UserMoveDefault(UserMoveDefault&&) = default;
|
||||
};
|
||||
|
||||
struct UserMoveAssignDefault {
|
||||
UserMoveAssignDefault(UserMoveAssignDefault&&) = default;
|
||||
UserMoveAssignDefault& operator=(UserMoveAssignDefault&&) = default;
|
||||
};
|
||||
|
||||
struct UserCopy{
|
||||
UserCopy(const UserCopy&);
|
||||
};
|
||||
|
||||
struct UserCopyDefault{
|
||||
UserCopyDefault(const UserCopyDefault&) = default;
|
||||
};
|
||||
|
||||
|
||||
struct UserDeletedMove{
|
||||
UserDeletedMove(UserDeletedMove&&) = delete;
|
||||
UserDeletedMove(const UserDeletedMove&) = default;
|
||||
};
|
||||
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(UserDtr));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(DefaultedDtr));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(UserMoveWithDefaultCopy));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(UserMove));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(UserCopy));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(UserMoveDefault));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(UserMoveAssignDefault));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(UserCopyDefault));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(UserDeletedMove));
|
||||
|
||||
template <typename T>
|
||||
class TestDependentErrors trivially_relocatable_if_eligible : T {};
|
||||
TestDependentErrors<Trivial> Ok;
|
||||
TestDependentErrors<NonRelocatable> Err;
|
||||
|
||||
struct DeletedMove {
|
||||
DeletedMove(DeletedMove&&) = delete;
|
||||
};
|
||||
struct DeletedCopy {
|
||||
DeletedCopy(const DeletedCopy&) = delete;
|
||||
};
|
||||
struct DeletedMoveAssign {
|
||||
DeletedMoveAssign& operator=(DeletedMoveAssign&&) = delete;
|
||||
};
|
||||
|
||||
struct DeletedDtr {
|
||||
~DeletedDtr() = delete;
|
||||
};
|
||||
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedMove));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedCopy));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedMoveAssign));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedDtr));
|
||||
|
||||
|
||||
union U {
|
||||
G g;
|
||||
};
|
||||
static_assert(!__is_trivially_copyable(U));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(U));
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct S {
|
||||
T t;
|
||||
};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(S<int>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(S<volatile int>));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(S<const int>));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(S<const int&>));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(S<int&>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(S<int[2]>));
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(S<const int[2]>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(S<int[]>));
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct SExplicit trivially_relocatable_if_eligible{
|
||||
T t;
|
||||
};
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(SExplicit<int>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(SExplicit<volatile int>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(SExplicit<const int>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(SExplicit<const int&>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(SExplicit<int&>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(SExplicit<int[2]>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(SExplicit<const int[2]>));
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(SExplicit<int[]>));
|
||||
|
||||
|
||||
namespace replaceable {
|
||||
|
||||
struct DeletedMove {
|
||||
DeletedMove(DeletedMove&&) = delete;
|
||||
};
|
||||
struct DeletedCopy {
|
||||
DeletedCopy(const DeletedCopy&) = delete;
|
||||
};
|
||||
struct DeletedMoveAssign {
|
||||
DeletedMoveAssign& operator=(DeletedMoveAssign&&) = delete;
|
||||
};
|
||||
|
||||
struct DefaultedMove {
|
||||
DefaultedMove(DefaultedMove&&) = default;
|
||||
DefaultedMove& operator=(DefaultedMove&&) = default;
|
||||
};
|
||||
struct DefaultedCopy {
|
||||
DefaultedCopy(const DefaultedCopy&) = default;
|
||||
DefaultedCopy(DefaultedCopy&&) = default;
|
||||
DefaultedCopy& operator=(DefaultedCopy&&) = default;
|
||||
};
|
||||
struct DefaultedMoveAssign {
|
||||
DefaultedMoveAssign(DefaultedMoveAssign&&) = default;
|
||||
DefaultedMoveAssign& operator=(DefaultedMoveAssign&&) = default;
|
||||
};
|
||||
|
||||
struct UserProvidedMove {
|
||||
UserProvidedMove(UserProvidedMove&&){};
|
||||
};
|
||||
struct UserProvidedCopy {
|
||||
UserProvidedCopy(const UserProvidedCopy&) {};
|
||||
};
|
||||
struct UserProvidedMoveAssign {
|
||||
UserProvidedMoveAssign& operator=(const UserProvidedMoveAssign&){return *this;};
|
||||
};
|
||||
|
||||
struct Empty{};
|
||||
static_assert(__builtin_is_replaceable(Empty));
|
||||
struct S1 replaceable_if_eligible{};
|
||||
static_assert(__builtin_is_replaceable(S1));
|
||||
|
||||
static_assert(__builtin_is_replaceable(DefaultedMove));
|
||||
static_assert(__builtin_is_replaceable(DefaultedCopy));
|
||||
static_assert(__builtin_is_replaceable(DefaultedMoveAssign));
|
||||
|
||||
static_assert(!__builtin_is_replaceable(DeletedMove));
|
||||
static_assert(!__builtin_is_replaceable(DeletedCopy));
|
||||
static_assert(!__builtin_is_replaceable(DeletedMoveAssign));
|
||||
static_assert(!__builtin_is_replaceable(DeletedDtr));
|
||||
|
||||
static_assert(!__builtin_is_replaceable(UserProvidedMove));
|
||||
static_assert(!__builtin_is_replaceable(UserProvidedCopy));
|
||||
static_assert(!__builtin_is_replaceable(UserProvidedMoveAssign));
|
||||
|
||||
struct DeletedCopyTpl {
|
||||
template <typename U>
|
||||
DeletedCopyTpl(const U&) = delete;
|
||||
};
|
||||
static_assert(__builtin_is_replaceable(DeletedCopyTpl));
|
||||
|
||||
|
||||
using NotReplaceable = DeletedMove;
|
||||
|
||||
template <typename T>
|
||||
struct WithBase : T{};
|
||||
|
||||
template <typename T>
|
||||
struct WithVBase : virtual T{};
|
||||
|
||||
struct WithVirtual {
|
||||
virtual ~WithVirtual() = default;
|
||||
WithVirtual(WithVirtual&&) = default;
|
||||
WithVirtual& operator=(WithVirtual&&) = default;
|
||||
};
|
||||
|
||||
static_assert(__builtin_is_replaceable(S<int>));
|
||||
static_assert(!__builtin_is_replaceable(S<volatile int>));
|
||||
static_assert(!__builtin_is_replaceable(S<const int>));
|
||||
static_assert(!__builtin_is_replaceable(S<const int&>));
|
||||
static_assert(!__builtin_is_replaceable(S<int&>));
|
||||
static_assert(__builtin_is_replaceable(S<int[2]>));
|
||||
static_assert(!__builtin_is_replaceable(S<const int[2]>));
|
||||
static_assert(__builtin_is_replaceable(WithBase<S<int>>));
|
||||
static_assert(!__builtin_is_replaceable(WithBase<S<const int>>));
|
||||
static_assert(!__builtin_is_replaceable(WithBase<UserProvidedMove>));
|
||||
static_assert(__builtin_is_replaceable(WithVBase<S<int>>));
|
||||
static_assert(!__builtin_is_replaceable(WithVBase<S<const int>>));
|
||||
static_assert(!__builtin_is_replaceable(WithVBase<UserProvidedMove>));
|
||||
static_assert(__builtin_is_replaceable(WithVirtual));
|
||||
|
||||
int n = 4; // expected-note 2{{declared here}}
|
||||
static_assert(!__builtin_is_cpp_trivially_relocatable(int[n]));
|
||||
// expected-warning@-1 {{variable length arrays in C++ are a Clang extension}}
|
||||
// expected-note@-2 {{read of non-const variable 'n' is not allowed in a constant expression}}
|
||||
static_assert(!__builtin_is_replaceable(int[n]));
|
||||
// expected-warning@-1 {{variable length arrays in C++ are a Clang extension}}
|
||||
// expected-note@-2 {{read of non-const variable 'n' is not allowed in a constant expression}}
|
||||
|
||||
|
||||
struct U1 replaceable_if_eligible {
|
||||
~U1() = delete;
|
||||
U1(U1&&) = default;
|
||||
U1& operator=(U1&&) = default;
|
||||
|
||||
};
|
||||
static_assert(!__builtin_is_replaceable(U1));
|
||||
|
||||
struct U2 replaceable_if_eligible {
|
||||
U2(const U2&) = delete;
|
||||
};
|
||||
static_assert(!__builtin_is_replaceable(U2));
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct WithVBaseExplicit replaceable_if_eligible : virtual T{};
|
||||
static_assert(__builtin_is_replaceable(WithVBaseExplicit<S<int>>));
|
||||
|
||||
struct S42 trivially_relocatable_if_eligible replaceable_if_eligible {
|
||||
S42(S42&&);
|
||||
S42& operator=(S42&&) = default;
|
||||
};
|
||||
struct S43 trivially_relocatable_if_eligible replaceable_if_eligible {
|
||||
S43(S43&&) = default;
|
||||
S43& operator=(S43&&);
|
||||
};
|
||||
|
||||
|
||||
struct Copyable1Explicit replaceable_if_eligible {
|
||||
Copyable1Explicit(Copyable1Explicit const &) = default;
|
||||
};
|
||||
|
||||
struct Copyable1 {
|
||||
Copyable1(Copyable1 const &) = default;
|
||||
};
|
||||
|
||||
|
||||
struct CopyAssign1Explicit replaceable_if_eligible {
|
||||
CopyAssign1Explicit & operator=(const CopyAssign1Explicit&) = default;
|
||||
};
|
||||
|
||||
struct CopyAssign1 {
|
||||
CopyAssign1 & operator=(CopyAssign1 const &) = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
void test__builtin_trivially_relocate() {
|
||||
struct S{ ~S();};
|
||||
struct R {};
|
||||
__builtin_trivially_relocate(); //expected-error {{too few arguments to function call, expected 3, have 0}}
|
||||
__builtin_trivially_relocate(0, 0, 0, 0); //expected-error {{too many arguments to function call, expected 3, have 4}}
|
||||
__builtin_trivially_relocate(0, 0, 0); //expected-error {{argument to '__builtin_trivially_relocate' must be a pointer}}
|
||||
__builtin_trivially_relocate((const int*)0, 0, 0); //expected-error {{argument to '__builtin_trivially_relocate' must be non-const}}
|
||||
__builtin_trivially_relocate((S*)0, 0, 0); //expected-error {{argument to '__builtin_trivially_relocate' must be relocatable}}
|
||||
__builtin_trivially_relocate((int*)0, 0, 0); //expected-error {{first and second arguments to '__builtin_trivially_relocate' must be of the same type}}
|
||||
|
||||
__builtin_trivially_relocate((int*)0, (int*)0, (int*)0); // expected-error-re {{cannot initialize a value of type '{{.*}}' with an rvalue of type 'int *'}}
|
||||
__builtin_trivially_relocate((int*)0, (int*)0, 0);
|
||||
__builtin_trivially_relocate((R*)0, (R*)0, 0);
|
||||
}
|
||||
|
||||
void test__builtin_trivially_relocate(auto&& src, auto&&dest, auto size) {
|
||||
__builtin_trivially_relocate(src, dest, size); // #reloc1
|
||||
}
|
||||
|
||||
void do_test__builtin_trivially_relocate() {
|
||||
struct S{ ~S();};
|
||||
struct R {};
|
||||
test__builtin_trivially_relocate((R*)0, (R*)0, 0);
|
||||
test__builtin_trivially_relocate((S*)0, (S*)0, 0);
|
||||
// expected-note@-1 {{'test__builtin_trivially_relocate<S *, S *, int>' requested here}}
|
||||
// expected-error@#reloc1 {{first argument to '__builtin_trivially_relocate' must be relocatable}}
|
||||
}
|
@ -75,7 +75,7 @@ static_assert(!__is_trivially_constructible(Holder<S3>, const Holder<S3>&));
|
||||
static_assert(!__is_trivially_assignable(Holder<S3>, const Holder<S3>&));
|
||||
static_assert(__is_trivially_destructible(Holder<S3>));
|
||||
static_assert(!__is_trivially_copyable(Holder<S3>));
|
||||
static_assert(!__is_trivially_relocatable(Holder<S3>));
|
||||
static_assert(__is_trivially_relocatable(Holder<S3>));
|
||||
static_assert(!__is_trivially_equality_comparable(Holder<S3>));
|
||||
|
||||
struct IA S4 {
|
||||
@ -97,7 +97,7 @@ static_assert(!__is_trivially_constructible(Holder<S4>, const Holder<S4>&));
|
||||
static_assert(!__is_trivially_assignable(Holder<S4>, const Holder<S4>&));
|
||||
static_assert(__is_trivially_destructible(Holder<S4>));
|
||||
static_assert(!__is_trivially_copyable(Holder<S4>));
|
||||
static_assert(!__is_trivially_relocatable(Holder<S4>));
|
||||
static_assert(__is_trivially_relocatable(Holder<S4>));
|
||||
static_assert(!__is_trivially_equality_comparable(Holder<S4>));
|
||||
|
||||
struct PA S5 {
|
||||
@ -119,5 +119,5 @@ static_assert(!__is_trivially_constructible(Holder<S5>, const Holder<S5>&));
|
||||
static_assert(!__is_trivially_assignable(Holder<S5>, const Holder<S5>&));
|
||||
static_assert(__is_trivially_destructible(Holder<S5>));
|
||||
static_assert(!__is_trivially_copyable(Holder<S5>));
|
||||
static_assert(!__is_trivially_relocatable(Holder<S5>));
|
||||
static_assert(__is_trivially_relocatable(Holder<S5>));
|
||||
static_assert(!__is_trivially_equality_comparable(Holder<S5>));
|
||||
|
@ -280,7 +280,12 @@ C++23, informally referred to as C++26.</p>
|
||||
<tr>
|
||||
<td>Trivial Relocatability</pre></td>
|
||||
<td><a href="https://wg21.link/P2786">P2786R13</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="partial" align="center">
|
||||
<details>
|
||||
<summary>Clang 21 (Partial)</summary>
|
||||
The feature test macro (<code>__cpp_trivial_relocatability</code>) has not yet been set.
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>#embed</pre></td>
|
||||
|
Loading…
x
Reference in New Issue
Block a user