-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[Clang] Implement the core language parts of P2786 - Trivial relocation #127636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
You can test this locally with the following command:git-clang-format --diff HEAD~1 HEAD --extensions h,cpp -- clang/test/CodeGenCXX/cxx2c-trivially-relocatable.cpp clang/test/Parser/cxx2c-trivially-relocatable.cpp clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp clang/include/clang/AST/ASTContext.h clang/include/clang/AST/DeclCXX.h clang/include/clang/AST/Type.h clang/include/clang/Parse/Parser.h clang/include/clang/Sema/Sema.h clang/lib/AST/ASTContext.cpp clang/lib/AST/Decl.cpp clang/lib/AST/DeclCXX.cpp clang/lib/AST/Type.cpp clang/lib/CodeGen/CGBuiltin.cpp clang/lib/Frontend/InitPreprocessor.cpp clang/lib/Parse/ParseDeclCXX.cpp clang/lib/Parse/Parser.cpp clang/lib/Sema/SemaChecking.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclCXX.cpp clang/lib/Sema/SemaExprCXX.cpp clang/test/SemaCXX/attr-trivial-abi.cpp clang/test/SemaCXX/ptrauth-triviality.cpp View the diff from clang-format here.diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 4a828e0b8..8bdc2300b 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5662,8 +5662,8 @@ static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) {
if (!BaseElementType->isObjectType())
return false;
- if(T.hasAddressDiscriminatedPointerAuth())
- return false;
+ if (T.hasAddressDiscriminatedPointerAuth())
+ return false;
if (const auto *RD = BaseElementType->getAsCXXRecordDecl();
RD && !RD->isPolymorphic() && IsCXXTriviallyRelocatableType(SemaRef, RD))
|
4fb6c28
to
9815133
Compare
TODO
|
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: cor3ntin (cor3ntin) ChangesThis adds
Patch is 52.91 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/127636.diff 25 Files Affected:
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 2a956ad5b2909..7e4e71299eb80 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1825,6 +1825,12 @@ The following type trait primitives are supported by Clang. Those traits marked
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
were made trivially relocatable via the ``clang::trivial_abi`` attribute.
+* ``__builtin_is_cpp_trivially_relocatable`` (C++): Returns true if and object
+ is trivially relocatable, as defined by the C++26 standard.
+ Note that the caller code should ensure that if the object is polymorphic,
+ the dynamic type is of the most derived type.
+* ``__builtin_is_replaceable`` (C++): Returns true if and object
+ is replaceable, as defined by the C++26 standard.
* ``__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
@@ -3624,6 +3630,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``
-----------------------------------
diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index 6620840df0ced..7633a987673e9 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -224,6 +224,10 @@ FIELD(StructuralIfLiteral, 1, NO_MERGE)
/// explicitly deleted or defaulted).
FIELD(UserProvidedDefaultConstructor, 1, NO_MERGE)
+FIELD(UserProvidedMoveAssignment, 1, NO_MERGE)
+FIELD(UserProvidedCopyAssignment, 1, NO_MERGE)
+FIELD(ExplicitlyDeletedMoveAssignment, 1, NO_MERGE)
+
/// The special members which have been declared for this class,
/// either by the user or implicitly.
FIELD(DeclaredSpecialMembers, 6, MERGE_OR)
@@ -253,4 +257,8 @@ FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE)
/// type that is intangible). HLSL only.
FIELD(IsHLSLIntangible, 1, NO_MERGE)
+FIELD(IsTriviallyRelocatable, 1, NO_MERGE)
+
+FIELD(IsReplaceable, 1, NO_MERGE)
+
#undef FIELD
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 266b93a64a390..4d578e3401456 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -127,6 +127,33 @@ class AccessSpecDecl : public Decl {
static bool classofKind(Kind K) { return K == AccessSpec; }
};
+enum class RelocatableOrReplaceableClassSpecifierKind {
+ Relocatable,
+ Replaceable
+};
+
+template <RelocatableOrReplaceableClassSpecifierKind MK>
+class BasicRelocatableOrReplaceableClassSpecifier {
+public:
+ BasicRelocatableOrReplaceableClassSpecifier() = default;
+ BasicRelocatableOrReplaceableClassSpecifier(SourceLocation Begin)
+ : Loc(Begin) {}
+ void Set(SourceLocation Begin) { Loc = Begin; }
+
+ bool isSet() const { return !Loc.isInvalid(); }
+
+ SourceLocation getLocation() const { return Loc; }
+
+private:
+ SourceLocation Loc;
+};
+
+using TriviallyRelocatableSpecifier =
+ BasicRelocatableOrReplaceableClassSpecifier<
+ RelocatableOrReplaceableClassSpecifierKind::Relocatable>;
+using ReplaceableSpecifier = BasicRelocatableOrReplaceableClassSpecifier<
+ RelocatableOrReplaceableClassSpecifierKind::Replaceable>;
+
/// Represents a base class of a C++ class.
///
/// Each CXXBaseSpecifier represents a single, direct base class (or
@@ -349,6 +376,10 @@ class CXXRecordDecl : public RecordDecl {
/// This is actually currently stored in reverse order.
LazyDeclPtr FirstFriend;
+ TriviallyRelocatableSpecifier TriviallyRelocatableSpecifier;
+
+ ReplaceableSpecifier ReplaceableSpecifier;
+
DefinitionData(CXXRecordDecl *D);
/// Retrieve the set of direct base classes.
@@ -717,11 +748,18 @@ class CXXRecordDecl : public RecordDecl {
/// deleted.
bool defaultedMoveConstructorIsDeleted() const {
assert((!needsOverloadResolutionForMoveConstructor() ||
- (data().DeclaredSpecialMembers & SMF_MoveConstructor)) &&
- "this property has not yet been computed by Sema");
+ (data().DeclaredSpecialMembers & SMF_MoveConstructor)) &&
+ "this property has not yet been computed by Sema");
return data().DefaultedMoveConstructorIsDeleted;
}
+ bool defaultedMoveAssignmentIsDeleted() const {
+ assert((!needsOverloadResolutionForMoveAssignment() ||
+ (data().DeclaredSpecialMembers & SMF_MoveAssignment)) &&
+ "this property has not yet been computed by Sema");
+ return data().DefaultedMoveAssignmentIsDeleted;
+ }
+
/// \c true if a defaulted destructor for this class would be deleted.
bool defaultedDestructorIsDeleted() const {
assert((!needsOverloadResolutionForDestructor() ||
@@ -806,6 +844,18 @@ class CXXRecordDecl : public RecordDecl {
return data().UserDeclaredSpecialMembers & SMF_CopyConstructor;
}
+ bool hasUserProvidedCopyAssignment() const {
+ return data().UserProvidedCopyAssignment;
+ }
+
+ bool hasUserProvidedMoveAssignment() const {
+ return data().UserProvidedCopyAssignment;
+ }
+
+ bool hasExplicitlyDeletedMoveAssignment() const {
+ return data().ExplicitlyDeletedMoveAssignment;
+ }
+
/// Determine whether this class needs an implicit copy
/// constructor to be lazily declared.
bool needsImplicitCopyConstructor() const {
@@ -1471,6 +1521,24 @@ class CXXRecordDecl : public RecordDecl {
return isLiteral() && data().StructuralIfLiteral;
}
+ TriviallyRelocatableSpecifier getTriviallyRelocatableSpecifier() const {
+ return data().TriviallyRelocatableSpecifier;
+ }
+
+ ReplaceableSpecifier getReplaceableSpecifier() const {
+ return data().ReplaceableSpecifier;
+ }
+
+ bool isTriviallyRelocatable() const { return data().IsTriviallyRelocatable; }
+
+ void setIsTriviallyRelocatable(bool Set) {
+ data().IsTriviallyRelocatable = Set;
+ }
+
+ bool isReplaceable() const { return data().IsReplaceable; }
+
+ void setIsReplaceable(bool Set) { data().IsReplaceable = Set; }
+
/// Notify the class that this destructor is now selected.
///
/// Important properties of the class depend on destructor properties. Since
@@ -1905,6 +1973,13 @@ class CXXRecordDecl : public RecordDecl {
return K >= firstCXXRecord && K <= lastCXXRecord;
}
void markAbstract() { data().Abstract = true; }
+
+ void setTriviallyRelocatableSpecifier(TriviallyRelocatableSpecifier TRS) {
+ data().TriviallyRelocatableSpecifier = TRS;
+ }
+ void setReplaceableSpecifier(ReplaceableSpecifier MRS) {
+ data().ReplaceableSpecifier = MRS;
+ }
};
/// Store information needed for an explicit specifier.
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 1d9743520654e..8eb373cdd942a 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -1129,6 +1129,10 @@ class QualType {
/// Return true if this is a trivially relocatable type.
bool isTriviallyRelocatableType(const ASTContext &Context) const;
+ bool isCppTriviallyRelocatableType(const ASTContext &Context) const;
+
+ bool isReplaceableType(const ASTContext &Context) const;
+
/// Returns true if it is a class and it might be dynamic.
bool mayBeDynamicClass() const;
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 0e5df338dd2e5..237fa3a46f8c8 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -2823,6 +2823,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];
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c513dab810d1f..84ee963e48ff6 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1063,6 +1063,9 @@ def err_access_specifier_interface : Error<
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'">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f10af8f5bd6b2..2292afe5f9ecb 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12427,6 +12427,11 @@ def err_builtin_invalid_arg_type: Error <
"an 'int'|"
"a vector of floating points}1 (was %2)">;
+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<
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 397a5d95709fb..5846acff120fc 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -555,6 +555,8 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary
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)
// Embarcadero Expression Traits
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 335258d597028..304ad6dd25476 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -164,6 +164,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;
@@ -3169,6 +3171,17 @@ class Parser : public CodeCompletionHandler {
SourceLocation FriendLoc);
bool isCXX11FinalKeyword() const;
+
+ bool isCXX2CTriviallyRelocatableKeyword(Token Tok) const;
+ bool isCXX2CTriviallyRelocatableKeyword() const;
+ void ParseOptionalCXX2CTriviallyRelocatableSpecifier(
+ TriviallyRelocatableSpecifier &TRS);
+
+ bool isCXX2CReplaceableKeyword(Token Tok) const;
+ bool isCXX2CReplaceableKeyword() const;
+ void ParseOptionalCXX2CReplaceableSpecifier(ReplaceableSpecifier &MRS);
+
+ bool isClassCompatibleKeyword(Token Tok) const;
bool isClassCompatibleKeyword() const;
/// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index c55b964650323..4fd14d2b93636 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3960,20 +3960,29 @@ class Sema final : public SemaBase {
/// Invoked when we enter a tag definition that we're skipping.
SkippedDefinitionContext ActOnTagStartSkippedDefinition(Scope *S, Decl *TD);
+ TriviallyRelocatableSpecifier
+ ActOnTriviallyRelocatableSpecifier(SourceLocation Loc);
+
+ ReplaceableSpecifier ActOnReplaceableSpecifier(SourceLocation Loc);
+
/// ActOnStartCXXMemberDeclarations - Invoked when we have parsed a
/// C++ record definition's base-specifiers clause and are starting its
/// member declarations.
- void ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagDecl,
- SourceLocation FinalLoc,
- bool IsFinalSpelledSealed,
- bool IsAbstract,
- SourceLocation LBraceLoc);
+ void ActOnStartCXXMemberDeclarations(
+ Scope *S, Decl *TagDecl, SourceLocation FinalLoc,
+ bool IsFinalSpelledSealed, bool IsAbstract,
+ TriviallyRelocatableSpecifier TriviallyRelocatable,
+ ReplaceableSpecifier Replaceable, SourceLocation LBraceLoc);
/// ActOnTagFinishDefinition - Invoked once we have finished parsing
/// the definition of a tag (enumeration, class, struct, or union).
void ActOnTagFinishDefinition(Scope *S, Decl *TagDecl,
SourceRange BraceRange);
+ void CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D);
+
+ void CheckCXX2CReplaceable(CXXRecordDecl *D);
+
void ActOnTagFinishSkippedDefinition(SkippedDefinitionContext Context);
/// ActOnTagDefinitionError - Invoked when there was an unrecoverable
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 610207cf8b9a4..dd45aa9c8d5dc 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -4445,6 +4445,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:
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 7eff776882629..b80c8827ee839 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -103,13 +103,16 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
HasConstexprDefaultConstructor(false),
DefaultedDestructorIsConstexpr(true),
HasNonLiteralTypeFieldsOrBases(false), StructuralIfLiteral(true),
- UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
+ UserProvidedDefaultConstructor(false), UserProvidedMoveAssignment(false),
+ UserProvidedCopyAssignment(false), ExplicitlyDeletedMoveAssignment(false),
+ DeclaredSpecialMembers(0),
ImplicitCopyConstructorCanHaveConstParamForVBase(true),
ImplicitCopyConstructorCanHaveConstParamForNonVBase(true),
ImplicitCopyAssignmentHasConstParam(true),
HasDeclaredCopyConstructorWithConstParam(false),
HasDeclaredCopyAssignmentWithConstParam(false),
- IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsLambda(false),
+ IsAnyDestructorNoReturn(false), IsHLSLIntangible(false),
+ IsTriviallyRelocatable(false), IsReplaceable(false), IsLambda(false),
IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
HasODRHash(false), Definition(D) {}
@@ -1529,7 +1532,10 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
if (DD->isNoReturn())
data().IsAnyDestructorNoReturn = true;
}
-
+ if (SMKind == SMF_CopyAssignment)
+ data().UserProvidedCopyAssignment = MD->isUserProvided();
+ else if (SMKind == SMF_MoveAssignment)
+ data().UserProvidedMoveAssignment = MD->isUserProvided();
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
@@ -1551,6 +1557,9 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
if (!MD->isUserProvided())
data().DeclaredNonTrivialSpecialMembersForCall |= SMKind;
}
+
+ if (MD->isDeleted() && SMKind == SMF_MoveAssignment)
+ data().ExplicitlyDeletedMoveAssignment = true;
}
void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
@@ -1578,8 +1587,11 @@ void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
data().HasIrrelevantDestructor = false;
} else if (D->isCopyAssignmentOperator())
SMKind |= SMF_CopyAssignment;
- else if (D->isMoveAssignmentOperator())
+ else if (D->isMoveAssignmentOperator()) {
SMKind |= SMF_MoveAssignment;
+ if (!D->isIneligibleOrNotSelected() && D->isDeleted())
+ data().ExplicitlyDeletedMoveAssignment = true;
+ }
// Update which trivial / non-trivial special members we have.
// addedMember will have skipped this step for this member.
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 8c11ec2e1fe24..77700056d8952 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2862,6 +2862,30 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
}
}
+bool QualType::isCppTriviallyRelocatableType(const ASTContext &Context) const {
+ QualType BaseElementType = Context.getBaseElementType(*this);
+ if (BaseElementType->isIncompleteType())
+ return false;
+ else if (BaseElementType->isScalarType())
+ return true;
+ else if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
+ return RD->isTriviallyRelocatable();
+ return false;
+}
+
+bool QualType::isReplaceableType(const ASTContext &Context) const {
+ if (isConstQualified())
+ return false;
+ QualType BaseElementType = Context.getBaseElementType(getUnqualifiedType());
+ if (BaseElementType->isIncompleteType())
+ return false;
+ if (BaseElementType->isScalarType())
+ return true;
+ if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
+ return RD->isReplaceable();
+ return false;
+}
+
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
return !Context.getLangOpts().ObjCAutoRefCount &&
Context.getLangOpts().ObjCWeak &&
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index d57f491a20c8e..8e7e714960321 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4762,6 +4762,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));
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 77833f5d1defb..48140a6375eb0 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -757,7 +757,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_explicit_this_parameter", "202110L");
}
- // We provide those C++23 features as extensions in earlier language modes, so
+ // We provide those C++2b features as extensions in earlier language modes, so
// we also define their feature test macros.
if (LangOpts.CPlusPlus11)
Builder.defineMacro("__cpp_static_call_operator", "202207L");
@@ -768,6 +768,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");
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 43db715ac6d70..3...
[truncated]
|
9815133
to
875bddf
Compare
df6e58e
to
ad4a5a9
Compare
clang/lib/AST/Type.cpp
Outdated
@@ -2862,6 +2862,30 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const { | |||
} | |||
} | |||
|
|||
bool QualType::isCppTriviallyRelocatableType(const ASTContext &Context) const { | |||
QualType BaseElementType = Context.getBaseElementType(*this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iirc getBaseElementTypeUnsafe()
does less work, and we don’t care about cv qualifiers anyway here from what I can tell.
clang/lib/AST/Type.cpp
Outdated
} | ||
|
||
bool QualType::isReplaceableType(const ASTContext &Context) const { | ||
if (isConstQualified()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t we also need to check isVolatileQualified()
here?
struct UserDtr { | ||
~UserDtr(); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A test w/ a deleted dtor would be nice
|
||
void test__builtin_trivially_relocate() { | ||
struct S{ ~S();}; | ||
struct R {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a test w/ __builtin_trivially_relocate()
in a template, particularly w/ a dependent src/dest argument.
clang/include/clang/AST/DeclCXX.h
Outdated
SourceLocation getLocation() const { return Loc; } | ||
|
||
private: | ||
SourceLocation Loc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there intent for this to 'have' more to it? At the moment, this seems like it has weak justification over a SourceLocation
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It used to have more. But I kept it because I felt a string type was cleaner than a bunch of lone SourceLocations. I'd rather keep it, unless you feel strongly about it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like a lot of 'overhead' mentally to have a type here that all it does is contain a single thing? But I guess I don't feel super strongly.
clang/docs/LanguageExtensions.rst
Outdated
* ``__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 the caller code should ensure that if the object is polymorphic, | ||
the dynamic type is of the most derived type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we introduce another builtin for this instead of fixing the existing one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like a built in that has the standard behavior.
I think there is an interesting question as to whether the existing builtin should be modified to return true
if either a type is trivial_abi or relocatable by the standard. I think this would make sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it make a lot more sense to deprecate marking types as trivial_abi
that aren't trivially relocatable? AFIAICT that's a requirement anyways, which is why it has been added in the first place. Then we can just say "__is_trivially_relocatable returns only true for types that are according to the standard" instead of introducing a new builtin just for the old builtin to turn out to be more useful in the end anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be happy doing that but I'm afraid it would break existing code.
Right now
__is_cpp_trivially_relocatable => standard behavior
__is_trivially_relocatable => standard behavior + trivial_abi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand what breakage you're afraid of. Sure, a deprecation warning would break stuff with -Werror
, but that's nothing new. I'm not saying we should make __is_trivially_relocatable
return false if only [[clang::trivial_abi]]
is added. I'm saying we should fix the users code and warn (and eventually error) if they're not giving us the guarantee. I just don't see when we'd ever want to use __is_cpp_trivially_relocatable
over __is_trivially_relocatable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::is_trivially_relocatable
should not return true for a type that is not relocatable per the standard.
This gives us a few options, none of them great
- Introduce a new trait so that we can model the standard behavior
- Modify the existing trait to ignore
[[clang::trivial_abi]]
which will break code - Be not conforming
Having a warning does not solve the conformance issue.
I agree with you that we probably want
- Restrict the set of types that can be
[[clang::trivial_abi]]
(ie warn if[[clang::trivial_abi]]
is added to a non-relocatable type) - Converge to a single trait by deprecating the old trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corentin is right. Any attempt we make to modify __is_trivially_relocatable
is going to either be non-standard or break [[clang::trivial_abi]]
.
SO, we'd want to choose which we'd want to do. I very strongly don't believe we can be non-standard here, this is an important standardized feature, and clang::trivial_abi
isn't any better.
I am ok with us modifying clang::trivial_abi
over a number of releases(like 2-3 releases!) to make it be the same as the standards feature, but only if we can come to an agreement with the folks who are already using it.
SO in short, I think combining these builtins right now is untenable. Long-term, it is possible but only if we are willing/able to significantly break clang::trivial_abi
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made an issue for that #128725
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::is_trivially_relocatable
should not return true for a type that is not relocatable per the standard.
Why? It's our extension to define. Am I missing something that makes annotating a type [[clang::trivial_abi]]
instead of trivially_relocatable_if_eligible [[clang::trivial_abi]]
significantly different? AFAIK having a type marked [[clang::trivial_abi]]
requires it to be trivially relocatable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK having a type marked [[clang::trivial_abi]] requires it to be trivially relocatable.
It's not exactly the same semantics.
In particular, the standard traits allow for polymorphic types, any code that would do a memcpy without checking for that would lead to nasty UB.
How special members contribute to relocatability is also different, the standard requires non-deleted assignment.
That would also break code or be forever non-conforming and portable.
clang/lib/Sema/SemaDeclCXX.cpp
Outdated
return !Dtr->isDeleted(); | ||
} | ||
|
||
static bool hasDeletedDestructor(CXXRecordDecl *D) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to make hasDeletedDestructor
a method of CXXRecordDecl
? Maybe it will end up bein useful around codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
clang/lib/Parse/ParseDeclCXX.cpp
Outdated
if (isCXX2CTriviallyRelocatableKeyword(Tok)) { | ||
if (TriviallyRelocatable.isSet()) { | ||
auto Skipped = Tok; | ||
ConsumeToken(); | ||
Diag(Skipped, diag::err_duplicate_class_relocation_specifier) | ||
<< /*trivial_relocatable*/ 0 | ||
<< TriviallyRelocatable.getLocation(); | ||
} else { | ||
ParseCXX2CTriviallyRelocatableSpecifier(TriviallyRelocatable); | ||
} | ||
continue; | ||
} else if (isCXX2CReplaceableKeyword(Tok)) { | ||
if (Replacable.isSet()) { | ||
auto Skipped = Tok; | ||
ConsumeToken(); | ||
Diag(Skipped, diag::err_duplicate_class_relocation_specifier) | ||
<< /*replaceable*/ 1 << Replacable.getLocation(); | ||
} else { | ||
ParseCXX2CReplaceableSpecifier(Replacable); | ||
} | ||
continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a feeling that if TriviallyRelocatableSpecifier/ReplaceableSpecifier were not templated and instead had some enum "Kind" field, the following two almost identical branches could be rewritten into:
BasicRelocatableOrReplaceableClassSpecifier ToComplain;
if (isCXX2CTriviallyRelocatableKeyword(Tok))
ToComplain = TriviallyRelocatable;
else if (isCXX2CReplaceableKeyword(Tok))
ToComplain = Replacable;
if (ToComplain.isSet()) {
auto Skipped = Tok;
ConsumeToken();
Diag(Skipped, diag::err_duplicate_class_relocation_specifier)
<< /*trivial_relocatable*/ ToComplain.getKind()
<< TriviallyRelocatable.getLocation();
} else if (ToComplain.isNotEmpty()) {
(ToComplain.getKind)? ParseCXX2CTriviallyRelocatableSpecifier(ToComplain) : ParseCXX2CReplaceableSpecifier(ToComplain);
}
that is an entirely optional suggestion but that amount of else/if/{} for a technically same code just makes it look sad.
|
||
|
||
class A trivially_relocatable_if_eligible {}; | ||
// cxx11-warning@-1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just curious, why don't we say and test C++26 instead of C++2c?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C++26 isn't 'official' yet, so our outward messaging is 2c at least for another few meetings.
072d822
to
33adeff
Compare
33adeff
to
4d21645
Compare
* fix tests (class with a const member are not implicitly relocatable) * Factorize some code to only compute default-movable once
when the trait is queried, in order to avoid a performance regression. Unfortunately, this move the queries from ASTContext to Sema.
The C++26 standard relocatable type traits has slightly different semantics, so we introduced a new ``__builtin_is_cpp_trivially_relocatable`` when implementing trivial relocation in llvm#127636. However, having multiple relocatable traits would be confusing in the long run, so we deprecate the old trait. As discussed in llvm#127636 `__builtin_is_cpp_trivially_relocatable` should be used instead.
…on (llvm#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 llvm#127609
The C++26 standard relocatable type traits has slightly different semantics, so we introduced a new ``__builtin_is_cpp_trivially_relocatable`` when implementing trivial relocation in #127636. However, having multiple relocatable traits would be confusing in the long run, so we deprecate the old trait. As discussed in #127636 `__builtin_is_cpp_trivially_relocatable` should be used instead. --------- Co-authored-by: Aaron Ballman <[email protected]>
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 shouldstart_lifetime_as
)the
__builtin_is_cpp_trivially_relocatable
and__builtin_is_replaceable
traitsFixes #127609