[clang] [Clang][WIP] Trivial relocation (PR #127636)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 18 06:03:36 PST 2025
https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/127636
None
>From a8f3214fd5ffa11c9c8b020a8add59b4a4a93224 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] [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 | 52 +++++
clang/lib/Sema/SemaDecl.cpp | 25 +-
clang/lib/Sema/SemaDeclCXX.cpp | 214 ++++++++++++++++++
clang/lib/Sema/SemaExprCXX.cpp | 6 +
clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 +
21 files changed, 571 insertions(+), 42 deletions(-)
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 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..be2a68411ae1a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12427,6 +12427,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 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..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 aae61f612a4bc..7f36537fa667a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1875,6 +1875,55 @@ 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 +2366,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 362df485a025c..9a7e40879e241 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18307,11 +18307,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);
@@ -18329,6 +18337,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 121da4916ed43..136a7ff39d5bb 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3689,6 +3689,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();
More information about the cfe-commits
mailing list