[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