[clang] [Clang] Implement the core language parts of P2786 - Trivial relocation (PR #127636)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 21 02:12:49 PST 2025
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/127636
>From 72599a0b22bd45412c2ad350bfa9b36bfa414f29 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 18 Feb 2025 14:37:32 +0100
Subject: [PATCH 01/10] [Clang][WIP] Trivial relocation
---
.../clang/AST/CXXRecordDeclDefinitionBits.def | 8 +
clang/include/clang/AST/DeclCXX.h | 81 +++++-
clang/include/clang/AST/Type.h | 4 +
clang/include/clang/Basic/Builtins.td | 6 +
.../clang/Basic/DiagnosticParseKinds.td | 3 +
.../clang/Basic/DiagnosticSemaKinds.td | 4 +
clang/include/clang/Basic/TokenKinds.def | 2 +
clang/include/clang/Parse/Parser.h | 13 +
clang/include/clang/Sema/Sema.h | 19 +-
clang/lib/AST/Decl.cpp | 1 +
clang/lib/AST/DeclCXX.cpp | 20 +-
clang/lib/AST/Type.cpp | 24 ++
clang/lib/CodeGen/CGBuiltin.cpp | 1 +
clang/lib/Frontend/InitPreprocessor.cpp | 3 +-
clang/lib/Parse/ParseDeclCXX.cpp | 121 +++++++--
clang/lib/Parse/Parser.cpp | 2 +
clang/lib/Sema/SemaChecking.cpp | 51 ++++
clang/lib/Sema/SemaDecl.cpp | 25 +-
clang/lib/Sema/SemaDeclCXX.cpp | 214 +++++++++++++++
clang/lib/Sema/SemaExprCXX.cpp | 6 +
clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 +
.../Parser/cxx2c-trivially-relocatable.cpp | 8 +
.../SemaCXX/cxx2c-trivially-relocatable.cpp | 257 ++++++++++++++++++
23 files changed, 835 insertions(+), 42 deletions(-)
create mode 100644 clang/test/Parser/cxx2c-trivially-relocatable.cpp
create mode 100644 clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
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..c4701a407e714 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.
@@ -716,12 +747,19 @@ class CXXRecordDecl : public RecordDecl {
/// \c true if a defaulted move constructor for this class would be
/// deleted.
bool defaultedMoveConstructorIsDeleted() const {
- assert((!needsOverloadResolutionForMoveConstructor() ||
- (data().DeclaredSpecialMembers & SMF_MoveConstructor)) &&
- "this property has not yet been computed by Sema");
+ // assert((!needsOverloadResolutionForMoveConstructor() ||
+ // (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 c3ff7ebd88516..7e0956c0b891a 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 0e8b0189540bd..d81df6b60e62e 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 feef50812eca9..abe3e0b874750 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12415,6 +12415,10 @@ 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 <
+ "argument%select{s|||}0 to '__builtin_trivially_relocate' must be"
+ "%select{a pointer|non-const|relocatable|the same}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..72dc9cf4ef96e 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(__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 093e9a06b00ce..3c79e7d10761e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3976,20 +3976,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 5a3be1690f335..c8dd60bbe3a13 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -4449,6 +4449,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 4625bf8088be6..bc7314f10fe00 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4767,6 +4767,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 e1dc728558def..ababb4f695098 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..3ed94fdf02012 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -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) {
@@ -2692,16 +2693,63 @@ 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::ParseOptionalCXX2CTriviallyRelocatableSpecifier(
+ TriviallyRelocatableSpecifier &TRS) {
+ assert(isCXX2CTriviallyRelocatableKeyword() &&
+ "expected a trivially_relocatable specifier");
+ TRS = Actions.ActOnTriviallyRelocatableSpecifier(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::ParseOptionalCXX2CReplaceableSpecifier(ReplaceableSpecifier &MRS) {
+ assert(isCXX2CReplaceableKeyword() &&
+ "expected a replaceable_if_eligible specifier");
+ MRS = Actions.ActOnReplaceableSpecifier(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();
+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(
@@ -3580,21 +3628,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
@@ -3808,13 +3854,39 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
SourceLocation AbstractLoc;
bool IsFinalSpelledSealed = false;
bool IsAbstract = false;
+ TriviallyRelocatableSpecifier TriviallyRelocatable;
+ ReplaceableSpecifier Replacable;
// Parse the optional 'final' keyword.
if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) {
while (true) {
VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok);
- if (Specifier == VirtSpecifiers::VS_None)
- break;
+ if (Specifier == VirtSpecifiers::VS_None) {
+ if (isCXX2CTriviallyRelocatableKeyword(Tok)) {
+ if (TriviallyRelocatable.isSet()) {
+ auto Skipped = Tok;
+ ConsumeToken();
+ Diag(Skipped, diag::err_duplicate_class_relocation_specifier)
+ << 0 << TriviallyRelocatable.getLocation();
+ } else {
+ ParseOptionalCXX2CTriviallyRelocatableSpecifier(
+ TriviallyRelocatable);
+ }
+ continue;
+ } else if (isCXX2CReplaceableKeyword(Tok)) {
+ if (Replacable.isSet()) {
+ auto Skipped = Tok;
+ ConsumeToken();
+ Diag(Skipped, diag::err_duplicate_class_relocation_specifier)
+ << 1 << Replacable.getLocation();
+ } else {
+ ParseOptionalCXX2CReplaceableSpecifier(Replacable);
+ }
+ continue;
+ } else {
+ break;
+ }
+ }
if (isCXX11FinalKeyword()) {
if (FinalLoc.isValid()) {
auto Skipped = ConsumeToken();
@@ -3850,7 +3922,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.isSet() || Replacable.isSet()) &&
"not a class definition");
// Parse any C++11 attributes after 'final' keyword.
@@ -3923,9 +3996,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, Replacable, 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
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 0710542f5e938..11e4c2913c93c 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -513,6 +513,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;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 74f425d32648f..41235059b4df0 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1875,6 +1875,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 ArgTy = TheCall->getArg(0)->getType();
+ if (!ArgTy->isPointerType() || ArgTy.getCVRQualifiers() != 0) {
+ S.Diag(TheCall->getArg(0)->getExprLoc(),
+ diag::err_builtin_trivially_relocate_invalid_arg_type)
+ << /*a pointer*/ 0;
+ return ExprError();
+ }
+
+ QualType T = ArgTy->getPointeeType();
+ if (S.RequireCompleteType(TheCall->getBeginLoc(), T,
+ diag::err_incomplete_type))
+ return ExprError();
+
+ if (T.isConstQualified() ||
+ !T.isCppTriviallyRelocatableType(S.getASTContext())) {
+ 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(ArgTy);
+
+ QualType Dest = TheCall->getArg(1)->getType();
+ if (Dest.getCanonicalType() != ArgTy.getCanonicalType()) {
+ S.Diag(TheCall->getArg(0)->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;
}
@@ -2317,6 +2365,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:
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 32cc9d33730d5..6c5ea3042a74f 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18315,11 +18315,19 @@ bool Sema::ActOnDuplicateDefinition(Decl *Prev, SkipBodyInfo &SkipBody) {
return true;
}
-void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD,
- SourceLocation FinalLoc,
- bool IsFinalSpelledSealed,
- bool IsAbstract,
- SourceLocation LBraceLoc) {
+TriviallyRelocatableSpecifier
+Sema::ActOnTriviallyRelocatableSpecifier(SourceLocation Loc) {
+ return {Loc};
+}
+
+ReplaceableSpecifier Sema::ActOnReplaceableSpecifier(SourceLocation Loc) {
+ return {Loc};
+}
+
+void Sema::ActOnStartCXXMemberDeclarations(
+ Scope *S, Decl *TagD, SourceLocation FinalLoc, bool IsFinalSpelledSealed,
+ bool IsAbstract, TriviallyRelocatableSpecifier TriviallyRelocatable,
+ ReplaceableSpecifier Replaceable, SourceLocation LBraceLoc) {
AdjustDeclIfTemplate(TagD);
CXXRecordDecl *Record = cast<CXXRecordDecl>(TagD);
@@ -18337,6 +18345,13 @@ void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD,
? FinalAttr::Keyword_sealed
: FinalAttr::Keyword_final));
}
+
+ if (TriviallyRelocatable.isSet() && !Record->isInvalidDecl())
+ Record->setTriviallyRelocatableSpecifier(TriviallyRelocatable);
+
+ if (Replaceable.isSet() && !Record->isInvalidDecl())
+ Record->setReplaceableSpecifier(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
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 664d48ccbc382..8214ea137b6e0 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7221,6 +7221,9 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
checkClassLevelDLLAttribute(Record);
checkClassLevelCodeSegAttribute(Record);
+ CheckCXX2CTriviallyRelocatable(Record);
+ CheckCXX2CReplaceable(Record);
+
bool ClangABICompat4 =
Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4;
TargetInfo::CallingConvKind CCK =
@@ -7258,6 +7261,217 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
}
}
+static bool hasSuitableConstructorForReplaceability(CXXRecordDecl *D,
+ bool Implicit) {
+ assert(D->hasDefinition() && !D->isInvalidDecl());
+
+ bool HasDeletedMoveConstructor = false;
+ bool HasDeletedCopyConstructor = false;
+ bool HasMoveConstructor = D->needsImplicitMoveConstructor();
+ bool HasDefaultedMoveConstructor = D->needsImplicitMoveConstructor();
+ bool HasDefaultedCopyConstructor = D->needsImplicitMoveConstructor();
+
+ for (const Decl *D : D->decls()) {
+ auto *MD = dyn_cast<CXXConstructorDecl>(D);
+ if (!MD || MD->isIneligibleOrNotSelected())
+ continue;
+
+ if (MD->isMoveConstructor()) {
+ HasMoveConstructor = true;
+ if (MD->isDefaulted())
+ HasDefaultedMoveConstructor = true;
+ if (MD->isDeleted())
+ HasDeletedMoveConstructor = true;
+ }
+ if (MD->isCopyConstructor()) {
+ if (MD->isDefaulted())
+ HasDefaultedCopyConstructor = true;
+ if (MD->isDeleted())
+ HasDeletedCopyConstructor = true;
+ }
+ }
+
+ if (HasMoveConstructor)
+ return !HasDeletedMoveConstructor &&
+ (Implicit ? HasDefaultedMoveConstructor : true);
+ return !HasDeletedCopyConstructor &&
+ (Implicit ? HasDefaultedCopyConstructor : true);
+ ;
+}
+
+static bool hasSuitableMoveAssignmentOperatorForReplaceability(CXXRecordDecl *D,
+ bool Implicit) {
+ assert(D->hasDefinition() && !D->isInvalidDecl());
+
+ if (D->hasExplicitlyDeletedMoveAssignment())
+ return false;
+
+ bool HasDeletedMoveAssignment = false;
+ bool HasDeletedCopyAssignment = false;
+ bool HasMoveAssignment = D->needsImplicitMoveAssignment();
+ bool HasDefaultedMoveAssignment = D->needsImplicitMoveAssignment();
+ bool HasDefaultedCopyAssignment = D->needsImplicitCopyAssignment();
+
+ for (const Decl *D : D->decls()) {
+ auto *MD = dyn_cast<CXXMethodDecl>(D);
+ if (!MD || MD->isIneligibleOrNotSelected())
+ continue;
+
+ if (MD->isMoveAssignmentOperator()) {
+ HasMoveAssignment = true;
+ if (MD->isDefaulted())
+ HasDefaultedMoveAssignment = true;
+ if (MD->isDeleted())
+ HasDeletedMoveAssignment = true;
+ }
+ if (MD->isCopyAssignmentOperator()) {
+ if (MD->isDefaulted())
+ HasDefaultedCopyAssignment = true;
+ if (MD->isDeleted())
+ HasDeletedCopyAssignment = true;
+ }
+ }
+
+ if (HasMoveAssignment)
+ return !HasDeletedMoveAssignment &&
+ (Implicit ? HasDefaultedMoveAssignment : true);
+ return !HasDeletedCopyAssignment &&
+ (Implicit ? HasDefaultedCopyAssignment : true);
+}
+
+void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) {
+ if (!D->hasDefinition() || D->isInvalidDecl())
+ return;
+
+ bool MarkedTriviallyRelocatable =
+ D->getTriviallyRelocatableSpecifier().isSet();
+
+ bool IsTriviallyRelocatable = true;
+ for (const CXXBaseSpecifier &B : D->bases()) {
+ const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
+ if (!BaseDecl)
+ continue;
+ if (B.isVirtual() ||
+ (!BaseDecl->isDependentType() && !BaseDecl->isTriviallyRelocatable())) {
+ IsTriviallyRelocatable = false;
+ }
+ }
+
+ for (const FieldDecl *Field : D->fields()) {
+ if (Field->getType()->isDependentType())
+ continue;
+ if (Field->getType()->isReferenceType())
+ continue;
+ QualType T = getASTContext().getBaseElementType(
+ Field->getType().getUnqualifiedType());
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
+ if (RD->isTriviallyRelocatable())
+ continue;
+ IsTriviallyRelocatable = false;
+ break;
+ }
+ }
+
+ if (!D->isDependentType() && !MarkedTriviallyRelocatable) {
+ bool HasSuitableMoveCtr = D->needsImplicitMoveConstructor();
+ bool HasSuitableCopyCtr = false;
+ if (D->hasUserDeclaredDestructor()) {
+ const auto *Dtr = D->getDestructor();
+ if (Dtr && (!Dtr->isDefaulted() || Dtr->isDeleted()))
+ IsTriviallyRelocatable = false;
+ }
+ if (IsTriviallyRelocatable && !HasSuitableMoveCtr) {
+ for (const CXXConstructorDecl *CD : D->ctors()) {
+ if (CD->isMoveConstructor() && CD->isDefaulted() &&
+ !CD->isIneligibleOrNotSelected()) {
+ HasSuitableMoveCtr = true;
+ break;
+ }
+ }
+ }
+ if (!HasSuitableMoveCtr && !D->hasMoveConstructor()) {
+ HasSuitableCopyCtr = D->needsImplicitCopyConstructor();
+ if (!HasSuitableCopyCtr) {
+ for (const CXXConstructorDecl *CD : D->ctors()) {
+ if (CD->isCopyConstructor() && CD->isDefaulted() &&
+ !CD->isIneligibleOrNotSelected()) {
+ HasSuitableCopyCtr = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
+ !D->hasUserDeclaredCopyAssignment() &&
+ !D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) {
+ // Do nothing
+ }
+
+ else if (!HasSuitableMoveCtr && !HasSuitableCopyCtr)
+ IsTriviallyRelocatable = false;
+
+ else if (IsTriviallyRelocatable &&
+ ((!D->needsImplicitMoveAssignment() &&
+ (D->hasUserProvidedMoveAssignment() ||
+ D->hasExplicitlyDeletedMoveAssignment())) ||
+ (!D->hasMoveAssignment() &&
+ D->hasUserProvidedCopyAssignment()))) {
+ IsTriviallyRelocatable = false;
+ }
+ }
+
+ D->setIsTriviallyRelocatable(IsTriviallyRelocatable);
+}
+
+void Sema::CheckCXX2CReplaceable(CXXRecordDecl *D) {
+ if (!D->hasDefinition() || D->isInvalidDecl())
+ return;
+
+ bool MarkedCXX2CReplaceable = D->getReplaceableSpecifier().isSet();
+
+ bool IsReplaceable = true;
+
+ for (const CXXBaseSpecifier &B : D->bases()) {
+ const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
+ if (!BaseDecl)
+ continue;
+ if ((!BaseDecl->isDependentType() && !BaseDecl->isReplaceable()) ||
+ B.isVirtual())
+ IsReplaceable = false;
+ }
+
+ if (!hasSuitableConstructorForReplaceability(D, !MarkedCXX2CReplaceable) ||
+ !hasSuitableMoveAssignmentOperatorForReplaceability(
+ D, !MarkedCXX2CReplaceable)) {
+ IsReplaceable = false;
+ }
+
+ if (IsReplaceable) {
+ for (const FieldDecl *Field : D->fields()) {
+ if (Field->getType()->isDependentType())
+ continue;
+ if (Field->getType()->isReferenceType()) {
+ IsReplaceable = false;
+ break;
+ }
+ if (Field->getType().isConstQualified()) {
+ IsReplaceable = false;
+ break;
+ }
+ QualType T = getASTContext().getBaseElementType(
+ Field->getType().getUnqualifiedType());
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
+ if (RD->isReplaceable())
+ continue;
+ IsReplaceable = false;
+ }
+ }
+ }
+
+ D->setIsReplaceable(IsReplaceable);
+}
+
/// Look up the special member function that would be called by a special
/// member function for a subobject of class type.
///
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 34219e0235a74..379f2456e08f3 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5107,6 +5107,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
@@ -5678,6 +5680,10 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return T.isTriviallyRelocatableType(C);
case UTT_IsBitwiseCloneable:
return T.isBitwiseCloneableType(C);
+ case UTT_IsCppTriviallyRelocatable:
+ return T.isCppTriviallyRelocatableType(C);
+ case UTT_IsReplaceable:
+ return T.isReplaceableType(C);
case UTT_CanPassInRegs:
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl(); RD && !T.hasQualifiers())
return RD->canPassInRegisters();
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index fcb7671ed92f0..dc6df0f5e468f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3690,6 +3690,10 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
// Start the definition of this instantiation.
Instantiation->startDefinition();
+ Instantiation->setTriviallyRelocatableSpecifier(
+ Pattern->getTriviallyRelocatableSpecifier());
+ Instantiation->setReplaceableSpecifier(Pattern->getReplaceableSpecifier());
+
// The instantiation is visible here, even if it was first declared in an
// unimported module.
Instantiation->setVisibleDespiteOwningModule();
diff --git a/clang/test/Parser/cxx2c-trivially-relocatable.cpp b/clang/test/Parser/cxx2c-trivially-relocatable.cpp
new file mode 100644
index 0000000000000..7a575124a3b35
--- /dev/null
+++ b/clang/test/Parser/cxx2c-trivially-relocatable.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -std=c++2b -verify -fsyntax-only %s
+
+
+class A trivially_relocatable_if_eligible {};
+class E final trivially_relocatable_if_eligible {};
+class G trivially_relocatable_if_eligible final{};
+class I trivially_relocatable_if_eligible trivially_relocatable_if_eligible final {}; // expected-error {{class already marked 'trivially_relocatable_if_eligible'}}
+class trivially_relocatable_if_eligible trivially_relocatable_if_eligible {};
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
new file mode 100644
index 0000000000000..7d300dfa4847c
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -0,0 +1,257 @@
+// RUN: %clang_cc1 -std=c++2c -verify %s
+
+class Trivial {};
+struct NonRelocatable {
+ ~NonRelocatable();
+};
+static NonRelocatable NonRelocatable_g;
+
+class A trivially_relocatable_if_eligible {};
+class B trivially_relocatable_if_eligible : Trivial{};
+class C trivially_relocatable_if_eligible {
+ int a;
+ void* b;
+ int c[3];
+ Trivial d[3];
+ NonRelocatable& e = NonRelocatable_g;
+};
+class D trivially_relocatable_if_eligible : Trivial {};
+class E trivially_relocatable_if_eligible : virtual Trivial {};
+
+class F trivially_relocatable_if_eligible : NonRelocatable {};
+
+class I trivially_relocatable_if_eligible {
+ NonRelocatable a;
+ NonRelocatable b[1];
+ const NonRelocatable c;
+ const NonRelocatable d[1];
+};
+
+class J trivially_relocatable_if_eligible: virtual Trivial, NonRelocatable {
+ NonRelocatable a;
+};
+
+class G trivially_relocatable_if_eligible {
+ G(G&&);
+};
+
+class H trivially_relocatable_if_eligible {
+ ~H();
+};
+
+struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}}
+static_assert(__is_cpp_trivially_relocatable(Incomplete)); // expected-error {{incomplete type 'Incomplete' used in type trait expression}}
+static_assert(__is_cpp_trivially_relocatable(Trivial));
+static_assert(__is_cpp_trivially_relocatable(G));
+static_assert(__is_cpp_trivially_relocatable(H));
+static_assert(__is_cpp_trivially_relocatable(int));
+static_assert(__is_cpp_trivially_relocatable(void*));
+static_assert(!__is_cpp_trivially_relocatable(int&));
+static_assert(!__is_cpp_trivially_relocatable(Trivial&));
+static_assert(__is_cpp_trivially_relocatable(const Trivial));
+static_assert(__is_cpp_trivially_relocatable(Trivial[1]));
+
+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 UserCopy{
+ UserCopy(const UserCopy&);
+};
+
+struct UserCopyDefault{
+ UserCopyDefault(const UserCopyDefault&) = default;
+};
+
+
+struct UserDeletedMove{
+ UserDeletedMove(UserDeletedMove&) = delete;
+ UserDeletedMove(const UserDeletedMove&) = default;
+};
+
+static_assert(!__is_cpp_trivially_relocatable(UserDtr));
+static_assert(__is_cpp_trivially_relocatable(DefaultedDtr));
+static_assert(!__is_cpp_trivially_relocatable(UserMoveWithDefaultCopy));
+static_assert(!__is_cpp_trivially_relocatable(UserMove));
+static_assert(!__is_cpp_trivially_relocatable(UserCopy));
+static_assert(__is_cpp_trivially_relocatable(UserMoveDefault));
+static_assert(__is_cpp_trivially_relocatable(UserCopyDefault));
+static_assert(__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;
+};
+
+static_assert(!__is_cpp_trivially_relocatable(DeletedMove));
+static_assert(!__is_cpp_trivially_relocatable(DeletedCopy));
+static_assert(!__is_cpp_trivially_relocatable(DeletedMoveAssign));
+
+union U {
+ G g;
+};
+static_assert(!__is_trivially_copyable(U));
+static_assert(__is_cpp_trivially_relocatable(U));
+
+
+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(UserProvidedMove));
+static_assert(!__builtin_is_replaceable(UserProvidedCopy));
+static_assert(!__builtin_is_replaceable(UserProvidedMoveAssign));
+
+using NotReplaceable = DeletedMove;
+
+template <typename T>
+struct S {
+ T t;
+};
+
+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));
+
+
+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>>)); // expected-error {{failed}}
+
+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;
+};
+
+
+}
>From 86049aaa5de6b385e90a206fa266c5dced7731e2 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 20 Feb 2025 16:43:55 +0100
Subject: [PATCH 02/10] add sema tests for __builtin_trivially_relocate
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 +++--
.../test/SemaCXX/cxx2c-trivially-relocatable.cpp | 15 +++++++++++++++
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index abe3e0b874750..18f0a3ca92b96 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12416,8 +12416,9 @@ def err_builtin_invalid_arg_type: Error <
"a vector of floating points}1 (was %2)">;
def err_builtin_trivially_relocate_invalid_arg_type: Error <
- "argument%select{s|||}0 to '__builtin_trivially_relocate' must be"
- "%select{a pointer|non-const|relocatable|the same}0">;
+ "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">;
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
index 7d300dfa4847c..1ca641226a292 100644
--- a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -253,5 +253,20 @@ 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{{cannot initialize a value of type 'unsigned long' with an rvalue of type 'int *'}}
+ __builtin_trivially_relocate((int*)0, (int*)0, 0);
+ __builtin_trivially_relocate((R*)0, (R*)0, 0);
}
>From 7479ef266e134e1b88e95e6649609cad939e07a4 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 20 Feb 2025 16:53:56 +0100
Subject: [PATCH 03/10] rename __is_cpp_trivially_relocatable
---
clang/include/clang/Basic/TokenKinds.def | 2 +-
.../SemaCXX/cxx2c-trivially-relocatable.cpp | 44 +++++++++----------
2 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 72dc9cf4ef96e..5846acff120fc 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -555,7 +555,7 @@ 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(__is_cpp_trivially_relocatable, IsCppTriviallyRelocatable, KEYCXX)
+TYPE_TRAIT_1(__builtin_is_cpp_trivially_relocatable, IsCppTriviallyRelocatable, KEYCXX)
TYPE_TRAIT_1(__builtin_is_replaceable, IsReplaceable, KEYCXX)
// Embarcadero Expression Traits
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
index 1ca641226a292..4b2eabda47a70 100644
--- a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -40,16 +40,16 @@ class H trivially_relocatable_if_eligible {
};
struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}}
-static_assert(__is_cpp_trivially_relocatable(Incomplete)); // expected-error {{incomplete type 'Incomplete' used in type trait expression}}
-static_assert(__is_cpp_trivially_relocatable(Trivial));
-static_assert(__is_cpp_trivially_relocatable(G));
-static_assert(__is_cpp_trivially_relocatable(H));
-static_assert(__is_cpp_trivially_relocatable(int));
-static_assert(__is_cpp_trivially_relocatable(void*));
-static_assert(!__is_cpp_trivially_relocatable(int&));
-static_assert(!__is_cpp_trivially_relocatable(Trivial&));
-static_assert(__is_cpp_trivially_relocatable(const Trivial));
-static_assert(__is_cpp_trivially_relocatable(Trivial[1]));
+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(Trivial));
+static_assert(__builtin_is_cpp_trivially_relocatable(G));
+static_assert(__builtin_is_cpp_trivially_relocatable(H));
+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]));
struct UserDtr {
~UserDtr();
@@ -85,14 +85,14 @@ struct UserDeletedMove{
UserDeletedMove(const UserDeletedMove&) = default;
};
-static_assert(!__is_cpp_trivially_relocatable(UserDtr));
-static_assert(__is_cpp_trivially_relocatable(DefaultedDtr));
-static_assert(!__is_cpp_trivially_relocatable(UserMoveWithDefaultCopy));
-static_assert(!__is_cpp_trivially_relocatable(UserMove));
-static_assert(!__is_cpp_trivially_relocatable(UserCopy));
-static_assert(__is_cpp_trivially_relocatable(UserMoveDefault));
-static_assert(__is_cpp_trivially_relocatable(UserCopyDefault));
-static_assert(__is_cpp_trivially_relocatable(UserDeletedMove));
+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(UserCopyDefault));
+static_assert(__builtin_is_cpp_trivially_relocatable(UserDeletedMove));
template <typename T>
class TestDependentErrors trivially_relocatable_if_eligible : T {};
@@ -109,15 +109,15 @@ struct DeletedMoveAssign {
DeletedMoveAssign& operator=(DeletedMoveAssign&&) = delete;
};
-static_assert(!__is_cpp_trivially_relocatable(DeletedMove));
-static_assert(!__is_cpp_trivially_relocatable(DeletedCopy));
-static_assert(!__is_cpp_trivially_relocatable(DeletedMoveAssign));
+static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedMove));
+static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedCopy));
+static_assert(!__builtin_is_cpp_trivially_relocatable(DeletedMoveAssign));
union U {
G g;
};
static_assert(!__is_trivially_copyable(U));
-static_assert(__is_cpp_trivially_relocatable(U));
+static_assert(__builtin_is_cpp_trivially_relocatable(U));
namespace replaceable {
>From 9677699389e4b1dcf7c88715f18de710b5cde70c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 20 Feb 2025 17:09:02 +0100
Subject: [PATCH 04/10] add docs and fix variable names in SemaChecking
---
clang/docs/LanguageExtensions.rst | 21 +++++++++++++++++++++
clang/lib/Sema/SemaChecking.cpp | 12 ++++++------
2 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 86295a3146510..d9f1f56cb2ed8 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1826,6 +1826,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
@@ -3625,6 +3631,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/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 41235059b4df0..a9b0615c1c2ab 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1882,15 +1882,15 @@ static ExprResult BuiltinTriviallyRelocate(Sema &S, CallExpr *TheCall) {
if (S.checkArgCount(TheCall, 3))
return ExprError();
- QualType ArgTy = TheCall->getArg(0)->getType();
- if (!ArgTy->isPointerType() || ArgTy.getCVRQualifiers() != 0) {
+ 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 = ArgTy->getPointeeType();
+ QualType T = Dest->getPointeeType();
if (S.RequireCompleteType(TheCall->getBeginLoc(), T,
diag::err_incomplete_type))
return ExprError();
@@ -1903,10 +1903,10 @@ static ExprResult BuiltinTriviallyRelocate(Sema &S, CallExpr *TheCall) {
return ExprError();
}
- TheCall->setType(ArgTy);
+ TheCall->setType(Dest);
- QualType Dest = TheCall->getArg(1)->getType();
- if (Dest.getCanonicalType() != ArgTy.getCanonicalType()) {
+ QualType Src = TheCall->getArg(1)->getType();
+ if (Src.getCanonicalType() != Dest.getCanonicalType()) {
S.Diag(TheCall->getArg(0)->getExprLoc(),
diag::err_builtin_trivially_relocate_invalid_arg_type)
<< /*the same*/ 3;
>From d96754cb0ffc5367d0ce9275fc9fe40e6b977520 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 20 Feb 2025 18:38:22 +0100
Subject: [PATCH 05/10] restore assert, add codegen test
---
clang/include/clang/AST/DeclCXX.h | 12 ++++++------
clang/lib/Frontend/InitPreprocessor.cpp | 2 +-
.../CodeGenCXX/cxx2c-trivially-relocatable.cpp | 16 ++++++++++++++++
3 files changed, 23 insertions(+), 7 deletions(-)
create mode 100644 clang/test/CodeGenCXX/cxx2c-trivially-relocatable.cpp
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index c4701a407e714..4d578e3401456 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -747,16 +747,16 @@ class CXXRecordDecl : public RecordDecl {
/// \c true if a defaulted move constructor for this class would be
/// deleted.
bool defaultedMoveConstructorIsDeleted() const {
- // assert((!needsOverloadResolutionForMoveConstructor() ||
- // (data().DeclaredSpecialMembers & SMF_MoveConstructor)) &&
- // "this property has not yet been computed by Sema");
+ assert((!needsOverloadResolutionForMoveConstructor() ||
+ (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");
+ assert((!needsOverloadResolutionForMoveAssignment() ||
+ (data().DeclaredSpecialMembers & SMF_MoveAssignment)) &&
+ "this property has not yet been computed by Sema");
return data().DefaultedMoveAssignmentIsDeleted;
}
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index ababb4f695098..b47c8fc027ab8 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++2b features as extensions in earlier language modes, so
+ // We provide those C++23 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");
diff --git a/clang/test/CodeGenCXX/cxx2c-trivially-relocatable.cpp b/clang/test/CodeGenCXX/cxx2c-trivially-relocatable.cpp
new file mode 100644
index 0000000000000..17144cffb6476
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-trivially-relocatable.cpp
@@ -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);
+};
>From 1ecd8838a463b94e2a78f60603809a606ce1bafa Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 21 Feb 2025 10:31:01 +0100
Subject: [PATCH 06/10] address comments, fix tests
---
clang/lib/Sema/SemaDeclCXX.cpp | 8 ++++++--
clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp | 2 +-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 8214ea137b6e0..aa681389f613e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7340,9 +7340,11 @@ static bool hasSuitableMoveAssignmentOperatorForReplaceability(CXXRecordDecl *D,
}
void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) {
- if (!D->hasDefinition() || D->isInvalidDecl())
+ if (D->isInvalidDecl())
return;
+ assert(D->hasDefinition());
+
bool MarkedTriviallyRelocatable =
D->getTriviallyRelocatableSpecifier().isSet();
@@ -7425,9 +7427,11 @@ void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) {
}
void Sema::CheckCXX2CReplaceable(CXXRecordDecl *D) {
- if (!D->hasDefinition() || D->isInvalidDecl())
+ if (D->isInvalidDecl())
return;
+ assert(D->hasDefinition());
+
bool MarkedCXX2CReplaceable = D->getReplaceableSpecifier().isSet();
bool IsReplaceable = true;
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
index 4b2eabda47a70..3ffa88e7c954f 100644
--- a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -266,7 +266,7 @@ void test__builtin_trivially_relocate() {
__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{{cannot initialize a value of type 'unsigned long' with an rvalue of type 'int *'}}
+ __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);
}
>From 44d4a059bfefa8b0bd65778a75f226d3a39bfce0 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 21 Feb 2025 10:32:12 +0100
Subject: [PATCH 07/10] address comments
---
clang/lib/AST/Type.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 77700056d8952..cdaa12ba205bb 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2866,9 +2866,9 @@ bool QualType::isCppTriviallyRelocatableType(const ASTContext &Context) const {
QualType BaseElementType = Context.getBaseElementType(*this);
if (BaseElementType->isIncompleteType())
return false;
- else if (BaseElementType->isScalarType())
+ if (BaseElementType->isScalarType())
return true;
- else if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
+ if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
return RD->isTriviallyRelocatable();
return false;
}
>From 464dd6dfe1dc8b2b741945dbd2eb9e1db298a5a5 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 21 Feb 2025 10:33:09 +0100
Subject: [PATCH 08/10] format
---
clang/include/clang/AST/DeclCXX.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 4d578e3401456..eec5c87819896 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -748,15 +748,15 @@ 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");
+ (data().DeclaredSpecialMembers & SMF_MoveAssignment)) &&
+ "this property has not yet been computed by Sema");
return data().DefaultedMoveAssignmentIsDeleted;
}
>From 86137ed48c2d6b1b49e5b2d12b3c5926b83d8353 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 21 Feb 2025 10:57:11 +0100
Subject: [PATCH 09/10] add extension warning and tests
---
.../clang/Basic/DiagnosticParseKinds.td | 9 ++++++
clang/lib/Parse/ParseDeclCXX.cpp | 12 ++++++++
.../Parser/cxx2c-trivially-relocatable.cpp | 28 ++++++++++++++++++-
clang/www/cxx_status.html | 2 +-
4 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 84ee963e48ff6..a1d4478d58829 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1057,6 +1057,15 @@ def ext_ms_abstract_keyword : ExtWarn<
"'abstract' keyword is a Microsoft extension">,
InGroup<MicrosoftAbstract>;
+def ext_relocatable_keyword : ExtWarn<
+ "'%select{trivially_relocatable|replaceable}0_if_eligible' "
+ "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">;
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 3ed94fdf02012..2fbeaa705a35f 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2711,6 +2711,12 @@ void Parser::ParseOptionalCXX2CTriviallyRelocatableSpecifier(
TriviallyRelocatableSpecifier &TRS) {
assert(isCXX2CTriviallyRelocatableKeyword() &&
"expected a trivially_relocatable specifier");
+
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus26
+ ? diag::warn_relocatable_keyword
+ : diag::ext_relocatable_keyword)
+ << /*relocatable*/ 0;
+
TRS = Actions.ActOnTriviallyRelocatableSpecifier(ConsumeToken());
}
@@ -2731,6 +2737,12 @@ bool Parser::isCXX2CReplaceableKeyword() const {
void Parser::ParseOptionalCXX2CReplaceableSpecifier(ReplaceableSpecifier &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 = Actions.ActOnReplaceableSpecifier(ConsumeToken());
}
diff --git a/clang/test/Parser/cxx2c-trivially-relocatable.cpp b/clang/test/Parser/cxx2c-trivially-relocatable.cpp
index 7a575124a3b35..1bb1c536f8b36 100644
--- a/clang/test/Parser/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/Parser/cxx2c-trivially-relocatable.cpp
@@ -1,8 +1,34 @@
-// RUN: %clang_cc1 -std=c++2b -verify -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++2c -verify=expected -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx11 -fsyntax-only %s
+
class A trivially_relocatable_if_eligible {};
+// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
class E final trivially_relocatable_if_eligible {};
+// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
class G trivially_relocatable_if_eligible final{};
+// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
class I trivially_relocatable_if_eligible trivially_relocatable_if_eligible final {}; // expected-error {{class already marked 'trivially_relocatable_if_eligible'}}
+// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
class trivially_relocatable_if_eligible trivially_relocatable_if_eligible {};
+// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+
+class J replaceable_if_eligible{};
+// cxx11-warning at -1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+class K replaceable_if_eligible replaceable_if_eligible {}; // expected-error {{class already marked 'replaceable_if_eligible'}}
+// cxx11-warning at -1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+class replaceable_if_eligible replaceable_if_eligible {};
+// cxx11-warning at -1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+class L replaceable_if_eligible trivially_relocatable_if_eligible final {};
+// cxx11-warning at -1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+// cxx11-warning at -2 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+class M replaceable_if_eligible final trivially_relocatable_if_eligible {};
+// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+// cxx11-warning at -2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+class N final trivially_relocatable_if_eligible replaceable_if_eligible {};
+// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+// cxx11-warning at -2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+class O trivially_relocatable_if_eligible replaceable_if_eligible final {};
+// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+// cxx11-warning at -2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 2e2fecc418504..eb10c750be8ed 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -280,7 +280,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
<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="unreleased" align="center">Clang 21</td>
</tr>
<tr>
<td><pre>#embed</pre></td>
>From ad4a5a9779555c7c68af01ca99bfb4c62d44cc26 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 21 Feb 2025 11:12:22 +0100
Subject: [PATCH 10/10] changelog, docs, C++03 tests
---
clang/docs/LanguageExtensions.rst | 1 +
clang/docs/ReleaseNotes.rst | 2 ++
.../Parser/cxx2c-trivially-relocatable.cpp | 22 +++++++++++++------
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index d9f1f56cb2ed8..31feefa154e4b 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1645,6 +1645,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
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e1c61992512b5..4287c9673ad9d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -72,6 +72,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>`_.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/test/Parser/cxx2c-trivially-relocatable.cpp b/clang/test/Parser/cxx2c-trivially-relocatable.cpp
index 1bb1c536f8b36..255e3e4f4460d 100644
--- a/clang/test/Parser/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/Parser/cxx2c-trivially-relocatable.cpp
@@ -1,34 +1,42 @@
-// RUN: %clang_cc1 -std=c++2c -verify=expected -fsyntax-only %s
+// 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 at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
class E final trivially_relocatable_if_eligible {};
// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+// cxx03-warning at -2 {{'final' keyword is a C++11 extension}}
class G trivially_relocatable_if_eligible final{};
// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
-class I trivially_relocatable_if_eligible trivially_relocatable_if_eligible final {}; // expected-error {{class already marked 'trivially_relocatable_if_eligible'}}
-// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+// cxx03-warning at -2 {{'final' keyword is a C++11 extension}}
+class I trivially_relocatable_if_eligible trivially_relocatable_if_eligible final {};
+// expected-error at -1 {{class already marked 'trivially_relocatable_if_eligible'}}
+// cxx11-warning at -2 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+// cxx03-warning at -3 {{'final' keyword is a C++11 extension}}
class trivially_relocatable_if_eligible trivially_relocatable_if_eligible {};
// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
-
class J replaceable_if_eligible{};
// cxx11-warning at -1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
-class K replaceable_if_eligible replaceable_if_eligible {}; // expected-error {{class already marked 'replaceable_if_eligible'}}
-// cxx11-warning at -1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+class K replaceable_if_eligible replaceable_if_eligible {};
+// expected-error at -1 {{class already marked 'replaceable_if_eligible'}}
+// cxx11-warning at -2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
class replaceable_if_eligible replaceable_if_eligible {};
// cxx11-warning at -1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
class L replaceable_if_eligible trivially_relocatable_if_eligible final {};
// cxx11-warning at -1 {{'replaceable_if_eligible' keyword is a C++2c extension}}
// cxx11-warning at -2 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
+// cxx03-warning at -3 {{'final' keyword is a C++11 extension}}
class M replaceable_if_eligible final trivially_relocatable_if_eligible {};
// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
// cxx11-warning at -2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+// cxx03-warning at -3 {{'final' keyword is a C++11 extension}}
class N final trivially_relocatable_if_eligible replaceable_if_eligible {};
// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
// cxx11-warning at -2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+// cxx03-warning at -3 {{'final' keyword is a C++11 extension}}
class O trivially_relocatable_if_eligible replaceable_if_eligible final {};
// cxx11-warning at -1 {{'trivially_relocatable_if_eligible' keyword is a C++2c extension}}
// cxx11-warning at -2 {{'replaceable_if_eligible' keyword is a C++2c extension}}
+// cxx03-warning at -3 {{'final' keyword is a C++11 extension}}
More information about the cfe-commits
mailing list